JSON Web Token and sliding expiration in a web application

In web applications, the most common authentication method to date has been the use of cookies, which store the server session identifier and have their own expiration date. At the same time, it is possible to automatically renew this date the next time a user contacts the server. This approach is called sliding expiration.

However, recently developers have been trying to refuse the use of cookies and server sessions for a number of reasons and are looking for alternative authentication methods. One of them is the use of JSON Web Token (JWT) - a token that contains in encrypted form all the minimum necessary information for authentication and authorization. At the same time, it is not necessary to store user data in the session, as the marker is self-contained. However, this in turn adds certain difficulties to the control over JWT, which can negate all its advantages over cookies. On the Internet, I have found several solutions to these problems, and here I would like to offer an alternative option, which, it seems to me, with its simplicity, should satisfy the needs of many projects.

The main reasons why developers could refuse cookies and sessions, in my opinion, are as follows:
  • Increasingly, developers are switching to single-page web applications (SPA) and accessing their server through the API. They use the same API to serve mobile applications. And in order to unify the authentication approach, they prefer to use access tokens, since the use of cookies on mobile platforms is difficult.
  • When the web application is scaled horizontally (web farm), the problem arises of synchronizing session state between servers. For this, of course, there are solutions, but it is easier to create stateless applications that do not require the use of a session at all. JWT solves this problem.


JWT itself, like a cookie, has its own expiration date and in the simplest case is used as follows:

  1. The user requests access from your server (and, in general, from the Authorization Server) by sending him a username and password.
  2. Authorization Server checks the validity of the user and sends him an access token, which has a certain expiration date (for example, after 2 weeks).
  3. The user uses this access token to access resources on your server (and generally on the Resource Server).
  4. Upon the expiration date (after 2 weeks), the user will have to go through the authentication procedure again

The main disadvantage of this approach is that in the case of a short expiration period, the user will often have to enter a username and password (which is inconvenient and less secure in terms of frequent password forwarding). Alternatively, it is suggested that you simply use a long expiration period (for example, 1 year). However, this approach raises a number of problems:
  • If the access token is stolen (for example, through an XSS vulnerability), an attacker could gain access to the resource for a long period.
  • If the administrator wants to restrict the user’s rights or change his role, then the user will have to go through the authentication procedure again so that the access token is updated.

In order to solve the described problems, it is often proposed along with a short-term access token to additionally use the second long-running refresh token. At the same time, during authentication, the user receives a refresh token (with an expiration duration of, for example, 1 year) and access token (for example, with a duration of 30 minutes). And for access to resources, he still uses access token, but now after 30 minutes in order to get a new access token, he just needs to send his refresh token to the Authorization Server and he will send him a fresh access token in response, while once again checking User rights.

The refresh token approach complicates both client and server code quite a bit. At the same time, it requires storing all refresh tokens of users along with Client id and other additional information.

If you want the user to use the resource endlessly after logging in, it is proposed to implement sliding expiration for tokens. That is, in the simplest (first) case, when receiving an access token, the server, when approaching the expiration date (or each time), sends the user a new access token with a shifted date. Such an approach in the event of a token theft leads to the fact that an attacker can endlessly use a resource.

In the second case, the same thing is done, but only for the refresh token.

Here are actually all the approaches that I managed to find. I, in turn, would like to limit myself to just one access token for simplicity, but at the same time have sliding expiration and the ability to change permissions and restrict access token in case of its theft.

To do this, I would add a new RefreshDate field to the token (the date after which the token needs to be updated; it should be less than the expiration date, if specified) and there is only one field in the user table in the database — MinRefreshDate. This field should store the minimum RefreshDate date that is valid for the user. In order to update the token, MinRefreshDate must be nonempty and always must be less than the RefreshDate of the token itself that needs to be updated.

In this case, the use process would look something like this:

  1. Let's say today is January 01, 1789. Refresh period we take 3 days. MinRefreshDate not specified for user (NULL).
  2. The user sends the login / password to the Authorization Server for the first time and receives an access token in response, having RefreshDate = 01/04/89. At the same time, the server sees that MinRefreshDate is empty and makes it equal to 01/04/89.
  3. The user uses access token on January 1,2 and 3 to access the Resource Server.
  4. Admin changes user role on January 2nd.
  5. At the next user request on January 4th (or later), the Resource Server understands that it needs to update the access token and itself requests it from the Authorization Server.
  6. The Authorization Server checks that MinRefreshDate is not empty and less than the RefreshDate from the token, and also checks the current user rights and generates a fresh access token that has RefreshDate = 07.01.89 and a new user role.
  7. The Resource Server passes the user a new access token along with the resources.
  8. The user continues to use the new access token on January 4th and 5th under the new role.
  9. On the 6th, access token was stolen by an attacker. However, the user notices this (for example, if he received a notification that his profile wasn’t accessed from a regular ip or browser)
  10. On the same day, the user enters the profile settings and clicks something like 'close all sessions and exit.' This resets the MinRefreshDate for this user.
  11. On the 7th, an attacker tries to update a stolen token, but cannot, because MinRefreshDate = NULL.
  12. On the 8th, the user again performs the authentication procedure and sends the login / password. At the same time, it receives a new token with RefreshDate = 11.01.89. At the same time, the server sees that MinRefreshDate is empty and makes it equal to 01/11/89 (in the case of a date already filled, it does not)
  13. On the 9th, the attacker again tries to update the token (which has RefreshDate = 01/07/89), but cannot, because its RefreshDate is less than MinRefreshDate.

That's the whole solution. It still has problems associated with the time window until RefreshDate occurs for a stolen or token-updating role. Also, if the user does not notice that the token has been stolen, the attacker can safely update the token and use the resource on behalf of the user as much as he pleases. But all these problems can be partially solved by reducing the duration of the Refresh period (for example, up to 30 minutes) and tightened control over unusual user activity.

I have no illusions that this approach will be of interest to someone and will be applied in practice, but I would like to hear an opinion about how safe and suitable it is for real use.

PS: Of course, on a real project, additional security should be provided with SSL and a synchronization token (Anti-Forgery Token). Plus, instead of MinRefreshDate, one could use some unique sequence of characters (such as SessionToken). But in this case, the JWT would also have to additionally add the SessionToken field so that it could be validated. It is also possible to store for each user a set of SessionToken-s (which would be created during each authentication) in order to more flexibly control and limit specific tokens.

Thanks.

Also popular now: