JWT (JSON Web Token) automatic prolongation of expiration [node.js]
In the case where you handle the auth yourself (i.e don't use a provider like Auth0), the following may work:
- Issue JWT token with relatively short expiry, say 15min.
- Application checks token expiry date before any transaction requiring a token (token contains expiry date). If token has expired, then it first asks API to 'refresh' the token (this is done transparently to the UX).
- API gets token refresh request, but first checks user database to see if a 'reauth' flag has been set against that user profile (token can contain user id). If the flag is present, then the token refresh is denied, otherwise a new token is issued.
The 'reauth' flag in the database backend would be set when, for example, the user has reset their password. The flag gets removed when the user logs in next time.
In addition, let's say you have a policy whereby a user must login at least once every 72hrs. In that case, your API token refresh logic would also check the user's last login date from the user database and deny/allow the token refresh on that basis.
I would like to implement JWT-based authentication to our new REST API. But since the expiration is set in the token, is it possible to automatically prolong it? I don't want users to need to sign in after every X minutes if they were actively using the application in that period. That would be a huge UX fail.
But prolonging the expiration creates a new token (and the old one is still valid until it expires). And generating a new token after each request sounds silly to me. Sounds like a security issue when more than one token is valid at the same time. Of course I could invalidate the old used one using a blacklist but I would need to store the tokens. And one of the benefits of JWT is no storage.
I found how Auth0 solved it. They use not only JWT token but also a refresh token: https://docs.auth0.com/refresh-token
But again, to implement this (without Auth0) I'd need to store refresh tokens and maintain their expiration. What is the real benefit then? Why not have only one token (not JWT) and keep the expiration on the server?
Are there other options? Is using JWT not suited for this scenario?
JWT (JSON Web Token) automatic prolongation of expiration
I work at Auth0 and I was involved in the design of the refresh token feature.
It all depends on the type of application and here is our recommended approach.
A good pattern is to refresh the token before it expires.
Set the token expiration to one week and refresh the token every time the user open the web application and every one hour. If a user doesn't open the application for more than a week, they will have to login again and this is acceptable web application UX.
To refresh the token your API needs a new endpoint that receives a valid, not expired JWT and returns the same signed JWT with the new expiration field. Then the web application will store the token somewhere.
Most native applications do login once and only once.
The idea is that the refresh token never expires and it can be exchanged always for a valid JWT.
The problem with a token that never expires is that never means never. What do you do if you lose your phone? So, it needs to be identifiable by the user somehow and the application needs to provide a way to revoke access. We decided to use the device's name, e.g. "maryo's iPad". Then the user can go to the application and revoke access to "maryo's iPad".
Another approach is to revoke the refresh token on specific events. An interesting event is changing the password.
We believe that JWT is not useful for these use cases so we use a random generated string and we store it on our side.
I was tinkering around when moving our applications to HTML5 with RESTful apis in the backend. The solution that I came up with was:
- Client is issued with a token with a session time of 30 mins (or whatever the usual server side session time) upon successful login.
- A client-side timer is created to call a service to renew the token before its expiring time. The new token will replace the existing in future calls.
As you can see, this reduces the frequent refresh token requests. If user closes the browser/app before the renew token call is triggered, the previous token will expire in time and user will have to re-login.
A more complicated strategy can be implemented to cater for user inactivity (e.g. neglected an opened browser tab). In that case, the renew token call should include the expected expiring time which should not exceed the defined session time. The application will have to keep track of the last user interaction accordingly.
I don't like the idea of setting long expiration hence this approach may not work well with native applications requiring less frequent authentication.
There is no way I know of to arbitrarily invalidate a token without involving a database one way or another.
Be careful with Approach 2 if your service can be accessed on several devices. Consider the following scenario...
- User signs in with iPad, Token 1 issued and stored.
- User signs in on website. Token 2 issued. User logs out.
- User tries to use iPad, Token 1 was issued before user logged out from website, Token 1 now considered invalid.
You might want to look at the idea of refresh tokens although these require database storage too.
Also see here for a good SO discussion regarding a similar problem, particular IanB's solution which would save some db calls.
Proposed solution Personally, this is how I'd approach it...user authenticates, issued with access token with a short expiry (say 15 mins) and a refresh token valid either for a much longer period or indefinitely. Store a record of this refresh token in a db.
Whenever the user is 'active', issue a new auth token each time (valid for 15 mins each time). If the user is not active for over 15 minutes and then makes a request (so uses an expired jwt), check the validity of the refresh token. If it's valid (including db check) then issue a new auth token.
If a user 'logs out' either on a device or through a website then destroy both access refresh tokens client side and importantly revoke the validity of the refresh token used. If a user changes their password on any device, then revoke all their refresh tokens forcing them to log in again as soon as their access token expires. This does leave a 'window of uncertainty' but that's unavoidable without hitting a db every time.
Using this approach also opens up the possibility of users being able to 'revoke' access to specific devices if required as seen with many major web apps.
JSON Web Token (JWT) benefits over a database session token
The main difference is the session storage size and lookup work required from the server:
On the server side, JWT stores a single key in memory (or in config file) - called secret key. That key has two purposes, it enables creating new encrypted tokens and it also functions like a master key that "opens all locks"- or in real life verifies all tokens. As a result the server responds much faster to auth requests, because it doesn't matter if you have two or two million users logged in - the same number of records (one, that server key) will be used to authenticate all client requests.
Traditional authentication that stores user sessions in a database, creates a record in the db for every single user, which results in multiple keys. So if you have two million users logged in, the server will create two million records and with each client request the server needs to locate the relevant session record in the database*.
JWT leaves it up to the client side to store and handle the entire session/user object. It actually makes much more sense because every client handles their own data only, so it doesn't cause heavy lifting for the client side either.
As for what you wrote in your last paragraph, it's not just db calls that we save here. JWT is actually much more scalable because of its independent and lightweight nature, it doesn't fail as auth requests pile up and it allows the server to handle auth accross devices and services without managing sessions on the server side.
Security wise though, db sessions arguably have the upper hand: they can be more secure because of that latency, and are also less vulnerable to session hijacking after user logout.
*The db stored sessions method can be optimized with effective caching and by storing only the session id (as opposed to the entire user object) in a fast key/value server such as Redis. That said, I would still choose JWT method over db for most cases.