I have a Koa based Node.js backend for my personal/hobby application.
I implemented session handling with JWT tokens. The client (AngularJS) gets the token after a successful login and stores the token somewhere (currently in sessionStorage
but for the purposes of this question it shouldn't matter).
I have two questions:
When I need to update the user record which the JWT represents, say, the user turned on Two-factor authentication (2FA) so I asked him to provide his phone number and I'd like to set this phone number in the user's record. Currently, after a successful verification on the phone number I call my backend to update the user record and I create a new JWT token with the updated user record (I exclude sensitive information from the JWT token like the hashed password, but I'd like to include the phone number for client side usage). Is it okay to create a new token when some of the credentials change and update the existing client side token with this new token? Should I never-ever create another token, only to create the one and only upon successful authentication? How do I then update the payload in the token?
How should I handle expired JWT tokens? In my mind I have 3 (possible) scenarios:
2.1. The JWT is set to short living, say 15 minutes. If the backend server replies with a 401 Unauthenticated 'Invalid token' (I guess this is the default behavior of koa-jwt
) then I automatically log-out my client and require re-authentication. But I also set up a complementary middleware, which is the last in the chain on the backend to re-create the token with a refreshed expiry and the client would also replace the existing token with the refreshed one. So if the user is active and uses the application every protected API call, in case of success, would create a new token to replace the old token.
2.2. The JWT is set long-living, say 1 week, and if it expires I opt-in re-authentication from the client.
2.3. Copy https://tools.ietf.org/html/rfc6749#section-1.5. Here when creating the JWT token after a successful authentication we send an access_token as well as a refresh_token. When the access_token is expired and the server responds with HTTP 401 'invalid token' (koa-jwt
default) then the client sends the refresh_token to the backend to require a new access_token (and optionally a new refresh_token). In this case I don't fully understand how the refresh_token is verified against the old access_token to provide a new token? Or why do we need to have a refresh_token?
Any generic advice on the upper topics (JWT updates and JWT expiration) would be helpful.
Starting from the bottom, I would ignore refresh tokens as I don't think they will help you here. They are generally aimed at other scenarios where the client application can provide storage more secure than the user browser -- think native mobile applications or server-side web applications.
Refresh Tokens are long-lived. This means when a client gets one from a server, this token must be stored securely to keep it from being used by potential attackers, for this reason it is not safe to store them in the browser.
(emphasis is mine; source refresh tokens)
This means that option 2.3 is basically the same as 2.2, which is not a bad option. It's not uncommon to have web applications with long session duration. If your application is not highly sensitive it's acceptable to use long session to improve user experience. For example, Django uses a default of two weeks for the age of its session cookie. See SESSION_COOKIE_AGE.
The remaining option (2.1), is usually referred as sliding session. The session timeout is short, but as long as the user keeps using the application within that interval the session gets automatically renewed. This is possibly the most common approach, or at least the one I used most time, so I'm biased. The only thing I would note is that sliding session are usually implemented with opaque session identifiers stored client-side as cookies and then with the actual session data stored on the server.
Your approach is a bit different because you have a stateless JWT token (it contains actual user data) stored on browser local storage. Like you said, in order to update the token you'll have to generate a new one, because you'll have to generate a new signature.
The signature is used to verify that the sender of the JWT is who it says it is and to ensure that the message wasn’t changed in the way.
(emphasis is mine; source JSON web tokens)
Having said all that, I would consider the following:
Having said that, you should also consider that JWT's are not huge, but they will still be an overhead if you decide to be automatically renewing. You may mitigate this a little by choosing a session duration of 20 minutes and only perform automatic renewal after half the session has elapsed.
Another point is that a vulnerability like XSS in your application will expose the access token to an attacker as the injected scripts would be able to read from localStorage
/sessionStorage
, this can be another point in favor of HTTP only session cookie storage.