Revoke/invalidate a token with JWT - node.js

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.

Related

Why using passport local strategy coupled with jwt strategy

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.

Preventing man-in-the-middle attacks with user authentication (Node/Vue/Passport)

I currently have a webapp I'm writing in Node/Vuejs with Passport handling authentication, and I've run into a problem. I was thinking about how I have authentication currently set up and I realized I had a glaring security hole.
In short, I have my Vuex store hitting a local API endpoint /api/me. That endpoint does a simple return of req.user. For the sake of brevity, a typical response looks like this:
{
username: 'Bob',
roles: [] // normal user has no roles,
email: 'someguy#bob.com'
}
My admin route /admin has a beforeEnter check, as shown below, that incorporates this check using the Vuex store, so I can have a cached version of user data accessible on the frontend.
{
path: '/admin',
name: '/admin',
component: Admin,
beforeEnter: (to, from, next) => {
store.dispatch('getMe').then(() => {
if (store.getters.user.roles && store.getters.user.roles.includes('administrator')) {
next();
return;
}
next({ path: '/' });
});
}
}
Here's the thing though - I realized that someone could easily game the system. In fact, I tried it myself with a test, non-Adminstrator account, and I was able to get in by returning the following from a local server set up for this purpose in Postman:
{
username: 'Super Admin Joe',
roles: ['administrator'] // normal user has no roles,
email: 'admin#bob.com'
}
And viola! The user now has full access to admin pages.
My question is, how could I prevent against this?
I need to check that the user is authenticated on every page, but a potential attacker could quite easily proxy any request (in this case it's /api/me) to make themselves any user they want. They can login normally with their own account, open the Network tab and copy the response payload, then change the user data as they wish. There needs to be some sort of encryption between the frontend and backend when checking a users' logged-in status, I believe.
I tried thinking about how I could prevent this from happening, but anything on my end (server-side, at least) seems useless as any request could easily be redirected to an attacker's local machine.
Any advice on how to "sign" my requests to make sure they aren't being proxied? Thanks in advance!
You shouldn’t have to be signing the response body of an api request. The typical way to do authentication is to establish a signed session cookie that acts either as an identifier to session information in an external database, or contains session information itself. This cookie should be in the header of your response and passport should give you a way to administer this cookie without you even realizing it.
This way the user can’t tamper with the information sent from the server in a way that’s easy to detect, and since it’s a cookie it will automatically be sent with each request by your browser (although if you’re using some AJAX library you may have to explicitly specify you’d like to send the cookie). What MadEard was referring to in the comment is where the cookie information is able to be accessed using passprt which is the ‘user’ property in the ‘req’ object.
After reading your github files:
server.get("/admin", function(req, res){
if(req.user && req.user.roles.includes("administrator")){
//user is an administrator, render the admin panel view
}else{
//is not an admin, redirect or send error message
}
});
In every Express route, after authentication with Passport, you have the req.user object.
It is established by checking the request cookie connect.sid, and checking which session this cookie belongs to on the server.
As such, you can trust that in any Express route, the object req.user contains the information relevant to that cookie and you can act upon it.
A little note: doing server-side validation should become a reflex for you over time.
The client is meant to display information. If, at any point, you are making the client take any decision that could be a security liability, take a step back and think it again.

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.

How to create sessions in a strong way and store them in cookies in node js?

In my login module once I log in I pass my request to a header to store a session here is the code
var series = rand.generate(16);
var token = rand.generate(16);
var loginToken = new LoginTokens({
userId: req.user._id,
series: series,
token: token
});
loginToken.save(function(err, l) {
if (err) {
console.log(err);
} else {
console.log(l);
res.cookie('loginToken', JSON.stringify({
series: series,
token: passwordHash.generate(token)
}));
res.status(200);
res.set('Content-Type', 'application/json');
res.end(JSON.stringify({
'success': req.params.res
}));
}
});
Though this code was pre-written and I don't understand it much(I don't get the point of randomly generating 16 digit number and saving it if somebody does please explain)
I have been tasked with implementing log out and As I don't understand the rationale behind above code I want to implement my own session module such that even if the same user logs in from a different device, both sessions will be maintained and logging out from one device will not log out someone from all device.
So now the point comes that the session id will not be based on user id. Also there is a module called express-session which I don't know if I should use or not, personally I feel that after reading it up on GitHub that what it will do is create a session id for me, which again I will have to save in my database, so why to import a module, instead I will base64 encode a random number like above and use it.
So what I want to ask is the real question
How do you implement sessions in node js?
My understanding of life cycle
A user signs up you see if he/she exists in the database and if he does you raise an error message and if he/she doesn't you create a username for him, probably by email id.
You then via code log him in. In that process, you first check if he is there in the database. If he is you go ahead take the _id field and try and create a session object. Ques what does your session table should look like? What all parameters should be present in sessions table? Then how do save them into res.cookies? Then what do you do with all those cookies, like on client side?
While implementing logout. My thinking is using passport.logout will remove the user object from request header but, I will have to first remove the entry from sessions table somehow empty the cookies and then do req.logout?
Can somebody please answer the above doubts?
I asked a very specific question but realised after the comment that what I need is logical clarity and I did not find one on youtube videos nor on SO posts, so if someone can share their views mostly in code, with some explanation it would be great.
Cookies get deleted by sending a new cookie with the same name (some invalid data would be good) and an expiry in the past.
passport.logout() removes the session cookie that passport uses to maintain the current user, the next request will no longer have the cookie and will appear as an invalid request.
I'm not sure why there's a loginToken as well as passport authentication, apart from allowing you to invalidate sessions server side (by deleting the appropriate token from the database) Check if your passport strategy configuration has any checks against LoginToken during it's deserialize user step (this is where passport takes the session cookie and turns it into a user against the request)

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