How to implement JWT auth on Loopback REST API? - node.js

I´m pretty new at nodejs, so I tried to implement an REST API with the loopback framework to try to simplify a bit the building process. I did correctly the models, also cusomized some endpoints, but, when connecting with the frontend (AngularJS), all the code I find, also the code I know to build, requires an JWT token to do any task that requires authorization/authentication, but it seems that loopback sends an uid2 token when I log in. I searched a lot, in stackoverflow and Github, the nearest thing I found is this (https://github.com/strongloop/loopback/issues/1905#issuecomment-310382322) but the code seems to fail here:
const data = jwt.verify(id, secretKey);
cb(null, {userId: data.id});
Any idea?

In simple words, you need to know who is making the call. If you'd use the default loopback authentication you would see that in the req object there is accessToken property which identifies the user. If you want to use JWT there are plenty of ready modules you could use, eg. loopback-jwt or loopback-jwt-advanced.
In general, what you need to do is to apply a middleware that will be responsible for authorization of your user( I strongly recommend you to get familiar with the middleware term). In simple words, middleware is a layer that your requests are going through and you can modify it's a body or reject the request.
In abstract steps, in your middleware you would have to:
get the token from the request
verify the token
identify the user based on the token
create the loopback AccessToken entity for that given user
put the token in the req.accessToken so now loopback will know who you are and you could use the ACL.
So this is more or less what those extensions are doing.

Related

Node.js: Authorizing routes vs. authorizing methods

Quick background
I am building an API with Node.js, Express and Mongoose. The authentication I implemented works with the passport-headerapikey package. I search the DB for the user with the api-key and add that user to the req Object. Thus ensuring my knowledge about the identity the whole time until the request ends.
Authorization
Let's get to the issue.
Up until now I called an authorize() function in every endpoint manually before doing anything. Like so:
router.post('/', (req, res) => {
autorize('admin', req.user.role) // method is called at every route manually
.then(() => {
... do stuff here
})
.catch(err => req.status(403).send())
}
My colleague said to me it is not a good solution and, rather than just securing the endpoint itself, I should have a session management that makes the current user available globally so that I can authorize at any point in every function individually.
Meaning:
A Method createUser(obj) could then call inside itself an authorization method or check for a condition like so:
createUser(obj) {
if (currentUser.role !== 'admin') {
return false
}
obj = new User(obj)
return obj.save()
}
That would mean I would return false in every function if a condition is met. Accessing the globally available currentUser for that session. (e.g. globalCurrentUser.role !== admin or something along those lines)
My question(s)
Is it bad practice to just secure endpoints and not functions?
Can't I just require an extra param "auth" with every function, so that when called it needs to receive the currentUser.role like in my authorize() function or it returns false? That means I pass the user manually to every function or it will simply fail
If I should have a session management for global access of the user during the request: Do you have suggestions for frameworks?
Thanks in advance,
Benno
Authentication and authorisation are two different things and should be treated separately. Authentication says "who are you?" and authorisation says "do you have permission?". Baring in mind that Express is designed entirely around middleware, I would do the following.
Abstract your authentication into a single piece of middleware which runs for all your endpoints using app.use() before you mount your router / routes.
Create an authorisation function which can be called from anywhere, it takes a user (or id or whatever you have) and a role, and it then checks if the user has that role.
Think of it like this, your authorisation will never change, it is core to your application. If you decided to ditch Expressjs and use Koa or move from traditional HTTP requests to Web Sockets you wouldn't want to change your authorisation logic. But your authentication may well change, you may wish to no longer use a header to store the token, perhaps you switch to cookies or something else entirely, you may wish to change this.
You'll end up with a piece of global middlware which checks an auth token and attaches the user object to the req. Then you'll have a utility function called something like userHasRole which will be called at any endpoint which requires a specific role within the application. You're then free to check permissions at any point in the application. This may be in very different places across your application, for instance you might check if they're an admin at the beginning of a request to some admin dashboard, but you might check permissions later on if they try to access a particular resource. When accessing a particular resource you might want to let them through and determine at the last minute if they have access to the resource. (It's hard to give a specific example without knowing more about your application).
In some instances it might be suitable to check at the beginning of the business logic, in other places it might make sense to check later on. This shouldn't matter, you should be able to run this check whenever you need to. This will depend entirely on the business logic and placing it in every single function ever may be useless if it's just formatting a string output, but it might be useful when trying to pull out a DB record.

Best way to handle API calls from frontend

Okay, so atm i have a frontend application built with Nuxt JS using Axios to do requests to my REST API(separate).
If a user does a search on the website the API URL is visible in XMLHttprequests so everyone could use the API if they want to.
What is the best way of making it so that only users that search through my website gets access to the API and people that just directly to the URL gets denied. I suppose using some sort of token system, but what is the best way to do it? JWT? (Users never log in so there is no "authentication")
Thanks!
IMO, you CANNOT block other illegal clients accessing your
backend as you describe that the official client and other illegal have the same knowledge about your backend.
But you can make it harder for illegal clients to accessing your backend through some approach such as POST all requests, special keys in header, 30-minutes-changed token in header and server-side API throttling by client IP.
If the security of the search API is really important, authenticate it by login; if not, just let it go since it is not in your critical path. Let's focus on other important things.
I'm in the same "boat" and my current setup is actually in VueJs but before even come to StackOverflow I developed a way to actually, the frontend calls the server and then the server calls the API, so in the browser, you will only see calls to the server layer that, the only constraint is that the call must come from the same hostname.
backend is handled with expressJs and frontend with VueJs
// protect /api calls to only be originated from 'process.env.API_ALLOW_HOST'
app.use(api.allowOnlySameDomainRequests());
...
const allowHostname = process.env.API_ALLOW_HOST ||'localhost';
exports.api = {
...
allowOnlySameDomainRequests: (req, res, next) => {
if(req.url.startsWith('/api') && req.hostname === allowHostname) {
// an /api call, only if request is the same
return next();
} else if (!req.url.startsWith('/api')) {
// not an /api call
return next();
}
return res.redirect('/error?code=401');
},
...
};
In our case, we use Oauth2 (Google sign through passportJs) to log in the user, I always have a user id that was given by the OAuth2 successful redirect and that user id is passed to the API in a header, together with the apikey... in the server I check for that userid permissions and I allow or not the action to be executed.
But even I was trying to find something better. I've seen several javascript frontend apps using calls to their backend but they use Bearer tokens.
As a curious user, you would see the paths to all the API and how they are composed, but in my case, you only see calls to the expressJs backend, and only there I forward to the real API... I don't know if that's just "more work", but seemed a bit more "secure" to approach the problem this way.

Can I use the passport-google callback to authenticate android/ios users?

I have a node.js server which authenticates using google-passport-oauth2. My server-side code looks like that from the documentation:
app.get('/auth/google',
passport.authenticate('google', { scope:
[ 'https://www.googleapis.com/auth/plus.login',
, 'https://www.googleapis.com/auth/plus.profile.emails.read' ] }
));
app.get( '/auth/google/callback',
passport.authenticate( 'google', {
successRedirect: '/auth/google/success',
failureRedirect: '/auth/google/failure'
}));
I figure that /auth/google redirects to google's login, and when permissions are recieved, the google profile and token are sent to the callback /auth/google/callback.
Now I am making an android app which wants to authenticate with this API. I'm using the directions for integrating Google Sign-In to do the authentication on google's end. Now my android app has the profile and token and wants to verify it with my server.
I've tried doing this with passport-google-token and passport-google-id-token (not sure the difference...), but it didn't work for whatever reason. Now I'm looking at other possibilities, like a Google Client API library for node.js, but it seems bulky. Then there's the tokeninfo endpoint, which involves an extra request and more latency. Or maybe I should look at express-jwt?
And suddenly, I wonder... couldn't I just pass the token from my android app to the server at auth/google/callback? That would make things a little simpler. I think this must be a pipe dream, because I haven't found any information about doing it. But if it's possible, how should I format the token/profile data in the request so the passport.authenticate() method recognizes it? (JSON, form data, headers)
If this can't be done, I'm taking suggestions for well-documented token verification libraries for node...
I still don't know about reusing the google-passport-oauth2 route, but I did figure out how to validate Google's idToken using passport-google-id-token.
The documentation says:
The post request to this route should include a JSON object with the
key id_token set to the one the client received from Google (e.g.
after successful Google+ sign-in).
But it only works if it's sent as a query string (GET or POST works).
https://localhost:8888/auth/googletoken?id_token=xxxxxxxxxx
I have a feeling this is not the most secure method, but I'll have to deal with that later.
EDIT: It turns out, the token is useless without the client ID (in your app), so it's OK to send it by querystring.
EDIT 2: One of the google-id-token devs has reminded me that the JSON will only be received if body-parser has been installed.

How to use passport js local and basic strategy on the same endpoint?

I'm using a MEAN Stack based on a Yeoman generator and I'm trying to password protect my api endpoints.
I want to be able to use local and basic strategies on the same endpoint so I can call the API from external sources and use it in my webapp.
However when I implemented this, I can use only one strategy a time because the basic authentication is "stateless" which means that doesn't attach a session.
So far I got 2 different endpoints with 2 different strategies with this code:
router.get('/all-basic', passport.authenticate(['local','basic'],{ session: false }), controller.index);
router.get('/all-local', auth.isAuthenticated(), controller.index);
I want to know if there any other approach or best practices to try to use 2 strategies on the same endpoint, or I just need to call 2 different endpoints.
Thanks!
Andres Osorio
After several attempts, I figured it out a solution.
I used the second endpoint:
router.get('/all-local', auth.isAuthenticated(), controller.index);
I used postman to make two requests:
One for the login attempt, (if you are using the Yeoman based MEAN Stack), the endpoint to call is:
/auth/local
Please note you should do a POST call with x-www-form-urlencoded and two key-value attributes (email and password). The endpoint will return a token that you need to store in order to make the subsequent calls.
The next step is to call the endpoint itself, I made a GET request to /all-local and I added a header called Authorization with the value Bearer [token], replace [token] with the token value from the first call.
Hope it helps.

Re-validating expired AccessToken (or get new one) when logging into app via Facebook Strategy using Passport

Under a variety of circumstances, Facebook's internal AccessToken (used for various features in the Facebook API and other cases) may become invalid. For example, if a user changes her password between sessions of using an application that relies on Facebook Login. There are a few other cases as well.
What happens, however, is that your application essentially crashes, with an error like this: data={"error":{"message":"Error validating access token: Session does not match current stored session. This may be because the user changed the password since the time the session was created or Facebook has changed the session for security reasons.", "type":"OAuthException", "code":190, "error_subcode":460}}.
Passport's Facebook Strategy uses the AccessToken, and it's available to an app as soon as a user is logged in / authenticated via Passport. The problem, however, is what to do when the above error is encountered. Facebook gives a convoluted re-auth flow as an example in PHP, but the general sense is that you need to re-authorize your app with Facebook.
The question is, even when removing the Facebook app from your Facebook page, and forcing the application relying on Facebook Login to re-authorize itself, it seems that Passport + Facebook Strategy is still picking up the last existing AccessToken from the browser's session storage. (At least that's what I'm seeing with Mozilla/Fx 26). I have re-authorized my app several times, but when debugging and looking at what Passport returns, I always get back the same invalid AccessToken. Seems like if an AccessToken exists in session-storage, Passport picks that up instead of getting a new one from Facebook.
So is there a way - within Passport + Facebook Strategy - to essentially ignore or override any stored AccessToken and always request a new one from Facebook, in the event of this kind of error? Not at all clear how to make that happen. Thanks for the help.
Update: The sample code for invoking the strategy has refreshToken as a parameter; what does this do? Is there a possible solution with this parameter?
passport.use(new FacebookStrategy(
{
...
},
function(accessToken, refreshToken, profile, done)
{
I found the answer to my question, and will leave it up in case anyone else encounters the same issue.
Passport, in its VerifyCallback function, passes the AccessToken to the application. The recommendation (tacit or otherwise) is to of course save this AccessToken as part of the user's persisted record (say, a MongoDB document). It'll be needed when making subsequent calls to the Facebook API, and though it can probably be safely retrieved from the request.user object passed around through the Express middleware, having your own local copy probably makes sense.
Now, when you're done authenticating the user via Passport, you still will pull up your user's persisted record to get other user data. Passport documentation features a generic function called Users.findOrCreate() to serve as a model/prototype of this function for you to implement. And here's where the issue lies: If you find a user (one already exists) via this function, you'll be using that record throughout your app. And if that record also holds an AccessToken, and that AccessToken has changed, then of course you'll be in trouble: Passport/Facebook is passing you a new AccessToken, but you're using an outdated one.
So the simple fix is this: If you've implemented a Users.findOrCreate() function to persist a user and rely on this to retrieve a user's set of data - and that record also stores the AccessToken - then make sure you check the current (old) AccessToken to the one Passport/Facebook is passing you in the VerifyCallback when a user has just been authenticated; if the two are different, update the user's persisted record and use this new Token.

Resources