89 lines
5.1 KiB
Plaintext
89 lines
5.1 KiB
Plaintext
:linkattrs:
|
|
|
|
|
|
== Refreshing a token
|
|
|
|
=== Introduction
|
|
|
|
In this section we touch upon refreshing an access token.
|
|
|
|
=== Types of tokens
|
|
|
|
In general there are two type of tokens: access token and a refresh token. The access token is used for making API
|
|
calls towards the server. Access tokens have a limited life span, that's where the refresh token comes in. Once
|
|
the access token is no longer valid a request can me made towards the server to get a new access token by presenting
|
|
the refresh token. The refresh token can expire but their life span is much longer. This solves the problem of a user
|
|
having to authenticate again with their credentials. Whether you should use a refresh token and access token depends,
|
|
below can find a couple of points to keep in mind while choosing which tokens to use.
|
|
|
|
So a normal flow can look like:
|
|
|
|
```
|
|
curl -X POST -H -d 'username=webgoat&password=webgoat' localhost:8080/WebGoat/login
|
|
```
|
|
|
|
The server returns:
|
|
|
|
```
|
|
{
|
|
"token_type":"bearer",
|
|
"access_token":"XXXX.YYYY.ZZZZ",
|
|
"expires_in":10,
|
|
"refresh_token":"4a9a0b1eac1a34201b3c5659944e8b7"
|
|
}
|
|
```
|
|
|
|
As you can see the refresh token is a random string which the server can keep track of (in memory or store in a database)
|
|
in order to match the refresh token to the user the refresh token was granted to.
|
|
So in this case whenever the access token is still valid we can speak of a "stateless" session, there is
|
|
no burden on the server side to setup the user session, the token is self contained.
|
|
When the access token is no longer valid the server needs to query for the stored refresh token to make sure the token
|
|
is not blocked in any way.
|
|
|
|
Whenever the attacker gets a hold on an access token it is only valid for a certain amount of time (say 10 minutes). The
|
|
attacker then needs the refresh token to get a new access token. That is why the refresh token needs better protection.
|
|
It is also possible to make the refresh token stateless but this means it will become more difficult to see if
|
|
the user revoked the tokens.
|
|
After the server made all the validations it must return a new refresh token and a new access token to the client. The
|
|
client can use the new access token to make the API call.
|
|
|
|
|
|
=== What should you check for?
|
|
|
|
Regardless of the chosen solution you should store enough information on the server side to validate whether the user
|
|
is still trusted. You can think of many things, like store the ip address, keep track of how many times the refresh
|
|
token is used (using the refresh token multiple times in the valid time window of the access token might indicate strange
|
|
behavior, you can revoke all the tokens an let the user authenticate again).
|
|
Also keep track of which access token belonged to which refresh token otherwise an attacker might
|
|
be able to get a new access token for a different user with the refresh token of the attacker
|
|
(see https://emtunc.org/blog/11/2017/jwt-refresh-token-manipulation/ for a nice write up about how this attack works)
|
|
Also a good thing to check for is the ip address or geolocation of the user. If you need to give out a new token check
|
|
whether the location is still the same if not revoke all the tokens and let the user authenticate again.
|
|
|
|
=== Need for refresh tokens
|
|
|
|
Does it make sense to use a refresh token in a modern single page application (SPA)? As we have seen in the section
|
|
about storing tokens there are two options: web storage or a cookie which mean a refresh token is right beside an
|
|
access token, so if the access token is leaked chances are the refresh token will also be compromised. Most of the time
|
|
there is a difference of course. The access token is sent when you make an API call, the refresh token is only sent
|
|
when a new access token should be obtained, which in most cases is a different endpoint. If you end up on the same
|
|
server you can choose to only use the access token.
|
|
|
|
As stated above using an access token and a separate refresh token gives some leverage for the server not to check
|
|
the access token over and over. Only perform the check when the user needs a new access token.
|
|
It is certainly possible to only use an access token. At the server you store the exact same information you would
|
|
store for a refresh token, see previous paragraph. This way you need to check the token each time but this might
|
|
be suitable depending on the application. In the case the refresh tokens are stored for validation it is important to protect these tokens as well (at least
|
|
use a hash function to store them in your database).
|
|
|
|
=== JWT a good idea?
|
|
|
|
There are a lot of resources available which question the usecase for using JWT token for client to server authentication
|
|
with regards to cookies. The best place to use a JWT token is between server to server communication. In a normal web
|
|
application you are better of using plain old cookies. See for more information:
|
|
|
|
- http://cryto.net/~joepie91/blog/2016/06/13/stop-using-jwt-for-sessions/[stop-using-jwt-for-sessions, window="_blank"]
|
|
- http://cryto.net/~joepie91/blog/2016/06/19/stop-using-jwt-for-sessions-part-2-why-your-solution-doesnt-work/[stop-using-jwt-for-sessions-part-2-why-your-solution-doesnt-work, window="_blank"]
|
|
- http://cryto.net/~joepie91/blog/attachments/jwt-flowchart.png[flowchart, window="_blank"]
|
|
|