auth0 / node-jsonwebtoken

JsonWebToken implementation for node.js http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html
MIT License
17.64k stars 1.22k forks source link

Set token invalid #748

Open omidiu opened 3 years ago

omidiu commented 3 years ago

I know "JWT" authentication process is stateless and for logout we should use something like "Redis" for "blacklist". But if we'll have some functionality that can invalid token it would be so good. So if it could be possible it would be our pleasure. thanks

acecconato commented 3 years ago

I agree, It could be great to implement at least a blacklist natively

ghost commented 2 years ago

Based on which criteria would both of you like the blacklist to work?

As @omidiu states correctly, JWT are stateless. Therefore any blacklisting I can imagine of would be based on the content wrapped in the JWT and therefore be only validated after this library did its job.

So in my opinion this issue can be closed as this "problem" does not belong to this library.

acecconato commented 2 years ago

Thanks for your reply.

Finally, I created a system that works perfectly with an access token, a refresh token, and a revoked status working with a local storage cache through node-localstorage.

You can see it in this repository: https://github.com/acecconato/AnthonyCecconato_7_03092021/blob/main/app/middlewares/authenticate.middleware.js

In my Vue 3 application, I just have to intercept Axios response, and if it returns a 401 error and if my user has a refresh token, I refresh automatically the access token by requesting my api/refresh-token route.

In this way: The access token has a short expiration time. The refresh token has a long expiration time (remember me button), and a token can be revoked just by requesting the cache and not the database (I consider it's still stateless..?). When we want to ban or delete a user account, we just have to set the isRevoked parameter to true instead of false.

I think it can be great to have an all-in-one solution for this. I searched but couldn't find anything! How did you ban or delete a user without a revoke / blacklist system? If we use a cache, can we consider it's not stateless anymore? I don't think so, but I'm not an expert at all.

ghost commented 2 years ago

Hi @acecconato,

the only all-in-one solution which comes into my mind would be Laravel from PHP universe, but I would not recommened PHP as basis for a new project.

The concept of long and short living tokens is definitely a possibility to solve it. Of course this depends individually on the security level needed, but how long did you chose the expiration duration for both of the tokens?

From my point of view this is still not a perfect solution, as it is indeed not stateless any more. When you come to the point of scaling your application horizontally, so accross multiple seperate servers/instances, how do you know which of your servers/instances will handle the request? A load balancer will try to route it to the same server based on a set cookie, but the targeted server might be currently unavailable. The alternatively chosen server does not have the same localStorage "blacklist", so the token is accepted again... To solve this your servers would have to sync their localStorage, which is a lot more overhead than simply using a fast external in-memory database like e.g. Redis, which is accessible by all of your servers together, to evaluate the session validity on every HTTP call.

This is the way I am solving the issue: When a user logs in, I get the user_id from my document database and then create a session with a unique ID and save the key-value-pair of session_id and user_id in a Redis database with a time to live of 24h. Redis as in-memory database supports time-to-live values for keys, so it deletes the key automatically and therefore invalidates the session at server-side. To the user I wrap the session ID into an encrypted JWT which is stored as http-only cookie to an explicit domain and expires automatically after 24h, too. When a user call comes in, I validate the JWT towards issuer and expire properties, extract the session_id from the JWT and then call Redis to get the user_id. With this extracted user_id I call my document SQL/NoSQL database for the requested action. If the session has to be invalidated (e.g. as the user logs out), then I simply overwrite the Redis entry of session_id with an empty string, so the connection to the user_id is not given anymore. The fact that Redis deletes the keys after 24h is not an issue, as the JWT expires after 24h, too. If another user would get the same session_id, the first user cannot be authenticated as the new user, as his JWT already was invalidated due to its expire property.

One additional information: Have a look at CouchbaseDB - it combines Redis and a key-value/document database, as it is memory-first but with greatly choosable persistence levels for e.g. saving user documents at registration. With Couchbase I simply have the sessions as well as the user documents in the same database and don't call different databases. This is not an advertisement, I just really like the product and the fact that you can use it open-source for a cluster of up to 3 servers, which is sufficient for most use-cases without replication accross different geo-zones.

lyslim commented 1 year ago

When a user call comes in, I validate the JWT towards issuer and expire properties, extract the session_id from the JWT and then call Redis to get the user_id. With this extracted user_id I call my document SQL/NoSQL database for the requested action. If the session has to be invalidated (e.g. as the user logs out), then I simply overwrite the Redis entry of session_id with an empty string, so the connection to the user_id is not given anymore.

Doesn't this make the stateless authentication process become stateful like the blacklist way?

Asjas commented 1 year ago

Doesn't this make the stateless authentication process become stateful like the blacklist way?

Yes it does.

lyslim commented 1 year ago

Doesn't this make the stateless authentication process become stateful like the blacklist way?

Yes it does.

Also, cookie + sessions seems enough, why bother to wrap it with JWT? What would be the advantage of this comparing to the blacklist way?

Asjas commented 1 year ago

Also, cookie + sessions seems enough, why bother to wrap it with JWT?

You can use cookies + sessions instead of cookies + JWT.

What would be the advantage of this comparing to the blacklist way?

There really isn't. You end up with a similar result.