Why using passport local strategy coupled with jwt strategy - node.js

Hi I'm trying to build a NestJS API and wanna learn the best practices to do it.
So I was wondering why the documentation of Nest JS
https://docs.nestjs.com/security/authentication
is providing an example of authentication with coupling passport-local and passport-jwt strategies?
Couldn't it be possible to just use the passport-jwt?
If the answer is, yes but it's not a good practice, could you provide explanations.
And if the answer is yes and it's the best practice, do you have any idea why they put an example that is not using best practices on such an important subject like authentication?

If I remember well the documentation, from the time I did my own authentication with Nest, the jwt and the local strategies are 2 ways to validate your users access but can be used in complementary.
The local strategy allow your server to validate the user with its username and password. So it will work well for a login endpoint, where the user fill the login form of your app.
Using this first step, you can use the jwt token api to create a token that will identify the user. You can set for example this token in a cookie, to be able to access it on your server when the user will make other requests.
Now, the jwt strategy will be perfect to validate the user by checking its datas and its signature in the token.
So to answer your question, you can of course use only one of this 2 strategies, but you will need to implement what they do on your own.
For example, the local strategy will be really easy to imitate by a simple endpoint in a resolver or a controller which check user information (username and password) in your DB, and set a cookie if succeed or respond an unauthorized if it failed
Hope what I say make sens !

Couldn't it be possible to just use the passport-jwt?
Yes, is possible use login controller without passport-local.
Passport-local only receibe two params (username and password) and you need implement validation, the returned value is pass how "user" propertie in Query object.
You can omit passport-local, implement the login controller with a loginDto in the #Body and validate credentials, are the same.
Example:
#Post('/auth/login')
async login(#Body() loginUserDto: LoginUserDto){
user = validation.....
if !(user) throw new UnauthorizedException();
return generateJwt(user);
}
where LoginUserDto can be
export class LoginUserDto {
#ApiProperty()
#IsString()
#IsLowercase()
#IsNotEmpty()
username: string;
#ApiProperty()
#IsNotEmpty()
#IsString()
password: string;
}
passport-jwt is only for validate the jwtoken sent by the user to privates routes of resources (endpoints).
The answer to the question if it is good practice, i don't know, for me is the same.

Related

How to implement JWT auth on Loopback REST API?

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.

CouchDB with Auth0

Is it possible to use Auth0 with CouchDB or Cloudant? If so, does anyone know a tutorial, code sample or github project for examples?
This question was already asked in the Auth0-Forum (not by me) but with no response: https://auth0.com/forum/t/can-you-use-auth0-with-couchdb-or-cloudant/3127
In my special case i want to connect a Ionic 2 App with Auth0 to a CouchDB instance without a middle (API) layer.
Any help would be appreciated!
There's the couch_jwt_auth plugin which allows users to authenticate via JWT tokens and hence allows this kind of scenario. There's even an example project on how to use it with pouchdb and Auth0, so guess this could be kind of useful for you.
I'm using the same plugin to authenticate users and it works flawlessly. I actually maintain a fork of it which allows JWT signature verification with asymmetric keys (RS256) - And yeah once I feel confident enough there'll be a pull request.
Will the user have its own database in couchdb?
Cause without a server side middleware you won't be able to limit access to only the users data.
If thats the case you could consider using oauth for it.
I am not deep into Auth0 but it seems to support it https://auth0.com/docs/oauth2-examples
as CouchDB too http://docs.couchdb.org/en/stable/api/server/authn.html#oauth-authentication
CouchDB 3.1 supports JWT auth out of the box.
The Couch docs on JWT auth are here.
Basically, update the [chttpd] section of the configuration to include the JWT section below. Well, you can delete default and/or cookie, but they're quite useful to keep around:
[chttpd]
authentication_handlers = {chttpd_auth, cookie_authentication_handler}, {chttpd_auth, jwt_authentication_handler}, {chttpd_auth, default_authentication_handler}
Then add this section, which is mentioned without example in the docs:
[jwt_auth]
; List of claims to validate
; As of couch 3.1, can NOT require issuer:
; required_claims = {iss, "http://issuer"} <-- does not work
; I left it blank and it's fine
required_claims =
and then add or uncomment this section:
[jwt_keys]
; Configure at least one key here if using the JWT auth handler.
; I found I had to inclue the 'kid' claim, even if it is just "_default".
; REMINDER: BASE64 ENCODED!
hmac:_default = aGVsbG8=
hmac:demo = aGVsbG8=
That's "hello", base64-encoded.
To get a JWT for this, do something like:
// once off...
const secret = 'hello'
// then, in an express-jwt handler somewhere...
const token = sign(data, secret, { subject: user.name, audience, algorithm, keyid: 'demo' });
return res.send({ error: null, token })
I found that subject (sub), audience (aud), algorithm (alg) and keyid (kid) all had to be included as JWT claims or CouchDB wouldn't accept the auth.
Try this: https://github.com/softapalvelin/couch_jwt_auth This plugin allows users to authenticate via JWT Tokens.

Revoke/invalidate a token with JWT

I'm using JWT (jsonwebtoken) package with node for token handling.
Is there any way to "logout"/revoke/invalidate a token?
If you just want to log the current user out, like #Massimiliano said, it's often implemented by deleting the token from whatever client-side store you've been using to keep track of it (eg. cookies).
If you need revocation for security reasons, here's an article that talks about why you might not want to go with JWT's in the first place, and instead use regular, opaque Bearer tokens (implemented by storing them in a database, often using a secure random string as their ID).
https://www.dinochiesa.net/?p=1388
Basically, they aren't revocable without adding a database lookup (negating one of the main reasons to use JWT's to begin with). And in the case of needing to revoke via a user interface, you're going to want to be able to query for tokens by user_id or group_id or similar, so you'll want to be able to index tokens by that information, instead of having it be opaque in the JWT claims.
with jwt authentication there is no need to insert the token in a database, if you want to revoke/logout/invalidate a token, just delete it locally in the browser there where you have decided to store it (cookie or header), in that case at the next call of whatever route (protected by authentication) the server, if had a good programmer, should ask you authentication again.
Hope this help, ciao.
I like that had this problem, I decided it this way:
First i create model session for db, like this:
var SessionSchema = new Schema({
user_id: { type: String, required: true },
tokenHash: { type: String, required: true }
});
After create token i save session, when user do something in my "checkSession" midleware i search token in db, if he exist i verify him, if not exist send auth_error, if token exist in db but expired, i remove him from db and send auth_error, if i delete or block user i remove session from db to.
Try, maybe this help you.

Passport.js: passport-facebook-token strategy, login through JS SDK and THEN authenticate passport?

I was looking for a way to let my client authorize with the facebook JS SDK and then somehow transfer this authorization to my node server (so it can verify requests with the fb graph api)
I stumbled across:
https://github.com/jaredhanson/passport-facebook/issues/26
&
https://github.com/drudge/passport-facebook-token
what seems to be an entirely different strategy from passport-facebook.
Am I correct when assuming that:
One logs in with the fb JS SDK, and then the facebook-token strategy somehow extracts the token and fb id from the document or body object?
Or is there any other decent way to achieve this? I'm namely trying to avoid the redirects enforced by the server SDKs
I've spent a couple of days this week trying to figure out the best way to use Facebook Authentication for a private API, using passport.js — passport-facebook-token is perfect for this.
You are correct in assuming these are two separate authentication strategies. You don't need passport-facebook installed to use passport-facebook-token.
If you have Facebook authentication implemented in the client-side JS (or iOS etc.), and are looking for a way to then authenticate API requests using your user's Facebook authToken, passport-facebook-token is a really elegant solution.
passport-facebook-token works totally independently of passport-facebook, and basically handles the redirects required by Facebook internally, before passing the request along to your controller.
So to authenticate an API route using passport-facebook-token, you'll need to set up a passport strategy like so:
passport.use('facebook-token', new FacebookTokenStrategy({
clientID : "123-your-app-id",
clientSecret : "ssshhhhhhhhh"
},
function(accessToken, refreshToken, profile, done) {
// console.log(profile);
var user = {
'email': profile.emails[0].value,
'name' : profile.name.givenName + ' ' + profile.name.familyName,
'id' : profile.id,
'token': accessToken
}
// You can perform any necessary actions with your user at this point,
// e.g. internal verification against a users table,
// creating new user entries, etc.
return done(null, user); // the user object we just made gets passed to the route's controller as `req.user`
}
));
It's worth noting that the User.findOrCreate method used in the passport-facebook-token Readme is not a default mongo/mongoose method, but a plugin that you'll have to install if you want it.
To use this auth strategy as middleware for any of your routes you'll need to pass it an access_token object either as a URL parameter, or as a property of the request body.
app.get('/my/api/:access_token/endpoint',
passport.authenticate(['facebook-token','other-strategies']),
function (req, res) {
if (req.user){
//you're authenticated! return sensitive secret information here.
res.send(200, {'secrets':['array','of','top','secret','information']});
} else {
// not authenticated. go away.
res.send(401)
}
}
NB. the access_token property is case-sensitive and uses an underscore.
The documentation for passport-facebook-token isn't extensive, but the source is really well commented and pretty easy to read, so I'd encourage you to take a look under the hood there. It certainly helped me wrap my head around some of the more general ways that passport works.

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