In web applications, the most common authentication method so far has been the use of cookies, which store the identifier of the server session and have their expiration date (expiration date). At the same time, it is possible to automatically extend this date upon the next access of the user to the server. This approach is called sliding expiration.
Recently, however, developers have sought to abandon the use of cookies and server session due to 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. There is no need to store user data in the session, since the self-contained marker is self-contained. However, this in turn adds certain difficulties with control over JWT, which can negate all its advantages over cookies. In the Internet, I have found several solutions to these problems, and here I would like to propose an alternative option, which, it seems to me, with its simplicity should satisfy the needs of many projects.
The main reasons why developers might refuse cookies and sessions are, in my opinion, the following:
- Increasingly, developers are switching to single-page web applications (SPAs) and accessing their server through the API. They use the same API to serve mobile applications. And in order to unify the approach to authentication, they prefer to use access tokens, since the use of cookies on mobile platforms is difficult.
- When a web application scales horizontally (web farm), there is a problem in synchronizing session state between servers. Of course, there are solutions for this, 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, as well as a cookie, has its own date of "puffing" (expiration date) and in the simplest case is used as follows:
')
- The user requests access from your server (and generally from the Authorization Server), sending him a username and password.
- The Authorization Server checks the user's validity and sends him an access token, which has a certain expiration date (for example, after 2 weeks).
- The user uses this access token to access resources on your server (and in general, on the Resource Server).
- Upon expiration date (after 2 weeks), the user will have to re-authenticate
The main disadvantage of this approach is that in the case of a short expiration period, the user will often have to enter a login and password (which is inconvenient and less secure in terms of frequently sending the password). As an option, it is proposed to simply use a long expiration period (for example, 1 year). However, this approach causes a number of problems:
- In case of theft of access token (for example, through XSS vulnerability), an attacker will be able to gain access to the resource for a long period.
- If the administrator wants to restrict the user rights or change his role, the user will have to go through the authentication procedure again in order for the access token to be updated.
In order to solve the problems described, it is often proposed, along with a short-term access token, to additionally use a second long-playing refresh token. In this case, during authentication, the user receives a refresh token (with a duration of expiration, for example, 1 year) and an access token (for example, with a duration of 30 minutes). And to access the resources, he still uses access token, but now after 30 minutes, in order to get a new access token, all he has to do is send his refresh token to the Authorization Server and he will send him a fresh access token in response, and once again check User rights.
The approach with the use of refresh token rather complicates both client and server code. At the same time, he needs to keep all refresh token users along with Client id and other additional information.
If you want the user to use the resource indefinitely after logging in, it is proposed to implement sliding expiration for tokens. That is, in the simplest (first) case, when an access token is received, the server, when approaching expiration date (or every time), sends the user a new access token with the date shifted. Such an approach in the case of the theft of a token, leads to the fact that the attacker can use the resource indefinitely.
In the second case, the same is done, but only for the refresh token.
That's all the approaches that I managed to find. I, in turn, would like to limit myself for simplicity to only one access token, but at the same time to have a sliding expiration and the ability to change the rights and restrict access to the token if it is stolen.
To do this, I would add a new RefreshDate field to the token (the date after which the token needs to be updated; must be less than expiration date if it is specified) and only one field - MinRefreshDate - to the database in the user table. This field should store the minimum RefreshDate date that is valid for the user. At the same time, to update the token, MinRefreshDate must be non-empty and must always be smaller than the RefreshDate of the token itself, which needs to be updated.
The process of use would look something like this:
- Suppose today is January 01, 1789. Refresh period, take 3 days. MinRefreshDate for user not specified (NULL).
- The user sends the login / password to the Authorization Server for the first time and receives in return an access token with RefreshDate = 04/01/89. At the same time, the server sees that MinRefreshDate is empty and makes it equal to 04/01/89.
- The user uses access token on January 1,2 and 3rd to access the Resource Server.
- The administrator changes the user role on January 2nd.
- At the next user request on the 4th of January (or later), the Resource Server understands that it is necessary to update the access token and itself requests it from the Authorization Server.
- The Authorization Server verifies that MinRefreshDate is not empty and less than the RefreshDate of the token, and also checks the current user rights and forms a fresh access token with RefreshDate = 07/01/89 and a new user role.
- Resource Server sends the user a new access token along with resources.
- The user continues to use the new access token on the 4th and 5th of January under the new role.
- On the 6th, the access token was stolen by the attacker. However, the user notices this (for example, if he received a notification that he did not come into his profile from a regular ip or browser)
- On the same day, the user enters the profile settings and presses something like 'close all sessions and exit'. This will reset MinRefreshDate for this user.
- On the 7th, the attacker tries to update the stolen token, but cannot, because MinRefreshDate = NULL.
- 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)
- On the 9th, the attacker again tries to update the token (for which RefreshDate = 07/01/89), but cannot, because his RefreshDate is smaller than MinRefreshDate.
That's all the decision. It still has problems associated with the time window before the RefreshDate has stolen or requires updating the role of the token. Also, if the user does not notice that the token was stolen, then the attacker can easily 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 enhanced control over the unusual activity of the user.
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 on how safe and suitable all this is for actual 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, you could use some unique sequence of characters (such as SessionToken). But in this case, the JWT would also have to additionally add a SessionToken field so that it can be validated. You can also store for each user a set of SessionTokens (which would be created with each authentication) in order to more flexibly control and limit specific tokens.
Thank.