295

I'm building a mobile app and am using JWT for authentication.

It seems like the best way to do this is to pair the JWT access token with a refresh token so that I can expire the access token as frequently as I want.

  1. What does a refresh token look like? Is it a random string? Is that string encrypted? Is it another JWT?
  2. The refresh token would be stored in the database on the user model for access, correct? It seems like it should be encrypted in this case
  3. Would I sent the refresh token back after a user login, and then have the client access a separate route to retrieve an access-token?
Hans Z.
  • 50,496
  • 12
  • 102
  • 115
jtmarmon
  • 5,727
  • 7
  • 28
  • 45
  • 8
    Note, if you are using refresh tokens you should provide an ability for users to invalidate them on the UI. It is also recommended to automatically expire them if they are not used for example for a month. – Vilmantas Baranauskas Jan 08 '15 at 07:45

5 Answers5

283

Below are the steps to do revoke your JWT access token:

  1. When you do log in, send 2 tokens (Access token, Refresh token) in response to the client.
  2. The access token will have less expiry time and Refresh will have long expiry time.
  3. The client (Front end) will store refresh token in an httponly cookie and access token in local storage.
  4. The client will use an access token for calling APIs. But when it expires, you call auth server API to get the new token (refresh token is automatically added to http request since it's stored in cookies).
  5. Your auth server will have an API exposed which will accept refresh token and checks for its validity and return a new access token.
  6. Once the refresh token is expired, the User will be logged out.

Please let me know if you need more details, I can share the code (Java + Spring boot) as well.

For your questions:

Q1: It's another JWT with fewer claims put in with long expiry time.

Q2: It won't be in a database. The backend will not store anywhere. They will just decrypt the token with private/public key and validate it with its expiry time also.

Q3: Yes, Correct

ATLChris
  • 3,198
  • 7
  • 39
  • 65
Bhupinder Singh
  • 3,355
  • 2
  • 11
  • 14
  • 78
    I think the JWT should be stored in ``localStorage`` and the ``refreshToken`` should be stored in a ``httpOnly``. The ``refreshToekn`` can be used to get a new JWT so it has to be handled with extra caution. – Tnc Andrei Jan 30 '19 at 13:22
  • 3
    Thanks what do you mean by storing in httpOnly? Why not store both in localStorage? – Jay Mar 01 '19 at 15:54
  • 51
    i'm missing the benefits of using the refresh token, wouldn't be the same to extend the validity of the access token? – user2010955 Apr 28 '19 at 13:44
  • 7
    @Jay According to the Microsoft Developer Network, HttpOnly is an additional flag included in a Set-Cookie HTTP response header. Using the HttpOnly flag when generating a cookie helps mitigate the risk of client side script accessing the protected cookie (if the browser supports it). – ns15 Jun 10 '19 at 09:58
  • 6
    @user2010955 access token will be sent by the client on every subsequent request , which means the probability someone capturing is high. – ns15 Jun 10 '19 at 10:05
  • 3
    The issue with storing the refreshtoken as a cookie as well is that although it's more secure than localstorage, it too will be sent over wire on every subsequent request, increasing the probability of compromise of the refreshtoken. Auth0 recommends to store the refresh token in memory only if there is no backend on the client application as opposed to storing it in localstorage which is vulnerable to XSS. https://auth0.com/docs/security/store-tokens#if-no-backend-is-present – Joseph Persico Jul 04 '19 at 07:08
  • I agree with @Tnc Andrei. If you not store the refresh token, you will never be able to invalidate to obtain new access token until the refresh token expired. I think it is better approach. – danipenaperez Aug 07 '19 at 09:09
  • 108
    #2 is highly inaccurate. A refresh token HAS to be stored on the server side. You shouldn't leverage the "self-contained" property of JWT for a refresh token. Doing so leaves you with no way to revoke refresh tokens other than changing your private key. – Jai Sharma Aug 16 '19 at 13:21
  • #Bhupinder, can you share the Java spring boot implementation you mentioned? – Vishnu Dahatonde Feb 02 '20 at 05:22
  • 3
    @VishnuDahatonde, vishnu-dahatonde, Here is the link: https://github.com/BhupinderSingh03/microService – Bhupinder Singh Feb 07 '20 at 16:31
  • 1
    When I read the following details from jwt, they have mentioned that refresh token should not be leaked to the client, since the hacker will get new access token based on the refresh token. Do we have any alternate? https://auth0.com/blog/refresh-tokens-what-are-they-and-when-to-use-them/ – Jeeva J Feb 10 '20 at 06:27
  • 6
    @JaiSharma-If the server stored the refresh token, how will the client request for a new access token when it expires as the client wont have the refresh token. Now do we need another api to fetch the refresh token for a logged in user then? Kindly clarify how would the process work in this case. – Subbu Feb 27 '20 at 12:56
  • How do i get refresh token. Please share some sample code for PHP. – Franklin Innocent F Aug 18 '20 at 04:20
  • 1
    How to implement feature allow user to force logout all devices if we don't store Access Token or Refresh Token on Server? – Robin Huy Nov 13 '20 at 04:54
  • 3
    as mentioned #2 is absolutely off the point. – Sean Ch Nov 30 '20 at 15:13
  • To implement the refresh token is the same way like we create the access token? But, we create the refresh token with the long expiry time? – heathscliff Dec 27 '21 at 09:10
  • 2
    can't an attacker get both the access token and refresh token from user's computer and use that to generate new access token to gain access as well? where is the protection here?!? – uberrebu Jan 01 '22 at 23:29
  • 3
    If the backend doesn't store the refresh token how does it get revoked? – Noobie3001 Jan 22 '22 at 01:42
  • 2
    Couple misunderstandings in this answer and comments. 1. (IMPORtANT) **Both refresh and access token should be stored in cookies** (ideally with `HttpOnly` flag). LocalStorage shouldn't be used for sensitive data, 2. It is not necessary to store both tokens at backend DBs (one of main advantages of JWT is ability to authenticate without access to DB. That allow to decrease DB overlaod). But if you don't store refresh tokens in DB one possible way to invalidate one token is change secret key and invalidate all tokens (all users will be logged out). For example github doing this trick sometimes – rzlvmp Jun 12 '22 at 11:48
  • local storage is not the best place for refresh tokens to be stored, it has many security issues. JWT might not be the best way to secure user data but rather it's about how we manage them, they should be stored in the server database, and with every request, the access token is added to the header and if it is expired refresh token API can be called to generate a new access_token which is used to authenticate user. – Antimatterr Sep 08 '22 at 06:57
  • 2
    If you are using a framework like React, which itself is xss-proof, and you can safely use localstorage to save any tokens imo. Secondly, being paranoid, I recreate my refresh token whenever `/refreshtoken` so previous refresh tokens become invalid. I also store last-issued JWT together with my refresh token in my DB, so whenever a `/refresh` is called, the last issued JWT should be present as well, other wise it's a invalid request and user gets logged out. – Someone Special Sep 12 '22 at 07:26
  • @SomeoneSpecial I actually like the idea of having the supposedly invalid access token be provided alongside a valid refresh token. I suppose it's possible to validate that (as in: yes, this was the last access token used). Though: if someone logs into the same account from multiple devices (or browsers), I suppose they'd have multiple access tokens and refresh tokens, right? Also if we do not store access tokens in local storage, we might have issues if someone uses multiple tabs in the same browser, right? – Igor Oct 15 '22 at 03:23
  • A user can have multiple token+refresh token for multiple devices. If you store localstorage then it will be saved across browser tabs (this is not a "issue" per say, it's what kind of behavior you want your client to have.) – Someone Special Oct 15 '22 at 04:50
  • 3
    It is not necessary to store the entire refresh token on the server side. For instance, you could use a JWT as a refresh token and store its `jti` (https://www.rfc-editor.org/rfc/rfc7519#page-10) claim on the server side. This has the advantage that you're not storing plaintext (refresh) tokens in your server database, but can still create a blocklist of revoked tokens. – OscarVanL Jan 28 '23 at 22:54
82

Assuming that this is about OAuth 2.0 since it is about JWTs and refresh tokens...:

  1. just like an access token, in principle a refresh token can be anything including all of the options you describe; a JWT could be used when the Authorization Server wants to be stateless or wants to enforce some sort of "proof-of-possession" semantics on to the client presenting it; note that a refresh token differs from an access token in that it is not presented to a Resource Server but only to the Authorization Server that issued it in the first place, so the self-contained validation optimization for JWTs-as-access-tokens does not hold for refresh tokens

  2. that depends on the security/access of the database; if the database can be accessed by other parties/servers/applications/users, then yes (but your mileage may vary with where and how you store the encryption key...)

  3. an Authorization Server may issue both access tokens and refresh tokens at the same time, depending on the grant that is used by the client to obtain them; the spec contains the details and options on each of the standardized grants

Hans Z.
  • 50,496
  • 12
  • 102
  • 115
  • 75
    2. You should store a hash of the refresh token in your database and then compare the hash of the user's refresh token with your stored hash. The rule of "don't store plain text passwords in your database" follows here. Consider a token like a random password that you made for the user. – tim-phillips Aug 17 '17 at 20:07
  • 11
    Also, If you want to provide more security, also perform refresh token rotation. The importance of this is already mentioned in the [ITEF RFC 6749](https://tools.ietf.org/html/rfc6749#page-47). If implemented correctly, this can also help in identifying the token theft scenario, i.e. refresh token been stolen by an attacker. If you are looking for a better explanation, head over to this [link](https://supertokens.io/blog/the-best-way-to-securely-manage-user-sessions?s=r) – Bhumil Sarvaiya Feb 10 '20 at 04:38
  • 2
    I do recommend following [this link](https://hasura.io/blog/best-practices-of-using-jwt-with-graphql/) to secure the JWT token and refresh token on the front-end. – Fouad Dec 12 '21 at 18:56
  • @Rohmer I understand whe it is needed to treat the refresh token as a password. But I wondering which is the advantage to use a refresh token instead of use the credentials (user and password) to get a new token? – Álvaro García Mar 18 '23 at 15:50
46

The refresh token flow is described in the OAuth 2.0 specification document.

  +--------+                                           +---------------+
  |        |--(A)------- Authorization Grant --------->|               |
  |        |                                           |               |
  |        |<-(B)----------- Access Token -------------|               |
  |        |               & Refresh Token             |               |
  |        |                                           |               |
  |        |                            +----------+   |               |
  |        |--(C)---- Access Token ---->|          |   |               |
  |        |                            |          |   |               |
  |        |<-(D)- Protected Resource --| Resource |   | Authorization |
  | Client |                            |  Server  |   |     Server    |
  |        |--(E)---- Access Token ---->|          |   |               |
  |        |                            |          |   |               |
  |        |<-(F)- Invalid Token Error -|          |   |               |
  |        |                            +----------+   |               |
  |        |                                           |               |
  |        |--(G)----------- Refresh Token ----------->|               |
  |        |                                           |               |
  |        |<-(H)----------- Access Token -------------|               |
  +--------+           & Optional Refresh Token        +---------------+
  
  
  (A)  The client requests an access token by authenticating with the
       authorization server and presenting an authorization grant.
  
  (B)  The authorization server authenticates the client and validates
       the authorization grant, and if valid, issues an access token
       and a refresh token.
  
  (C)  The client makes a protected resource request to the resource
       server by presenting the access token.
  
  (D)  The resource server validates the access token, and if valid,
       serves the request.
  
  (E)  Steps (C) and (D) repeat until the access token expires.  If the
       client knows the access token expired, it skips to step (G);
       otherwise, it makes another protected resource request.
  
  (F)  Since the access token is invalid, the resource server returns
       an invalid token error.
  
  (G)  The client requests a new access token by authenticating with
       the authorization server and presenting the refresh token.  The
       client authentication requirements are based on the client type
       and on the authorization server policies.
  
  (H)  The authorization server authenticates the client and validates
       the refresh token, and if valid, issues a new access token (and,
       optionally, a new refresh token).

Regarding your questions:

  1. It is another JWT
  2. The refresh token can be stored on the backend of the client‘s side, so that the user can not access it. Otherwise it has to be encrypted.
  3. You get the refresh token as well as the access token after the login. When your access token expires, you send the refresh token to the server to get new refresh and access tokens.
Dennis Meissel
  • 1,825
  • 1
  • 21
  • 33
  • 2
    What is the security risk to have both Access and Refresh Token handling on resource server ? – Kebechet Oct 30 '21 at 05:27
  • @ssamko it is less secure: if the access_token is stolen, it will quickly expire, if the refresh_token is stolen, someone will be able to produce many new access_tokens. So if the resource server for any reason had been hacked, someone would have been able to create new access_tokens. – Dennis Meissel Nov 09 '22 at 14:43
  • 1
    @ssamko but here it's not just about security, it is also the idea: the resource server checks only whether the client can access a resource and the client cares about the refreshing. Moreover, the resource server doesn't have to be connected to authentication server. It can be, but not necessarily. – Dennis Meissel Nov 09 '22 at 14:47
  • 1
    Could you explain why the refresh to token has to be stored on the server side? Do you mean it has to be stored in a db? And if so why? Why it cannot be will decrypt the token with private/public key? – Saba Dec 25 '22 at 13:01
  • 1
    @Saba, You are right, thank you for the comment. The refresh token should not be accessed directly by the user. If it is, it has to be encrypted. I‘ve updated the second point to get rid of ambiguity. – Dennis Meissel Dec 26 '22 at 14:42
  • It is not necessary to store the entire refresh token or encrypt it on the server side if you use a JWT as a refresh token and store its jti (rfc-editor.org/rfc/rfc7519#page-10) claim on the server side. This has the advantage that you're not storing plaintext (refresh) tokens in your server database, but can still create a blocklist of revoked tokens by blocklisting the `jti`, either when someone logs out, or if there is a breach. – OscarVanL Jan 28 '23 at 22:56
  • What i don't get is what happend to the request E? The steps F to H are clear, but who triggers the request E again? Right now the client never gets the expected response of E because of the invalid token. Does the user needs to request the data manualy again or is there a way that the same request with new token will be triggered again automaticly? – Thomas Mueller Aug 14 '23 at 12:57
  • @ThomasMueller after the client receives the invalid token error, it just needs to update the token: the flow goes to G and H steps and then back to C and D steps. Usually the refreshment happens in background so that the user doesn't notice it. – Dennis Meissel Aug 15 '23 at 09:34
34

Based in this implementation with Node.js of JWT with refresh token:

  1. In this case they use a uid and it's not a JWT. When they refresh the token they send the refresh token and the user. If you implement it as a JWT, you don't need to send the user, because it would be inside the JWT.

  2. They implement this in a separate document (table). It makes sense to me because a user can be logged in in different client applications and it could have a refresh token by app. If the user lose a device with one app installed, the refresh token of that device could be invalidated without affecting the other logged in devices.

  3. In this implementation, it response to the log in method with both, access token and refresh token. It seems correct to me.

Chetan Goyal
  • 435
  • 5
  • 14
David
  • 351
  • 3
  • 3
  • By saying "1) In this case they use a uid..." did you mean UUID? – ozanmuyes Aug 08 '18 at 14:15
  • What about this simpler implementation - Issue JWT - send the older JWT when you want to refresh - (you can check `iat` with window) - reissue a new one based on the previous one – adonese Aug 31 '19 at 10:16
  • @adonese by sending only the `JWT` you mean to have the `refresh_token` inside it? If so, OAuth RFC 6749 explicitly says to not send `refresh_token` to the resource server (and the `JWT` is sent to the resource servers): https://tools.ietf.org/html/rfc6749#section-1.5 – Brenno Costa Oct 23 '19 at 15:11
4

JWT has two problems:

  1. lousy standardization

  2. it is hard to revoke (when used for authentication)

The first can be solved by using your own JWT implementation: put in JSON whatever you want, encrypt it with AES - voila - use it for authentication (also for authorization if needed: put roles inside JSON).

Super minimalistic JWT {"id" : "<id>"}

The second problem requires clarification. With regular sessions which are stored on the server side, there is no revocation problem: the session can be invalidated by the server anytime. But regular sessions have problems with scalability and performance, hence JWT.

A common solution to the revocation problem is to use a refresh-token.

Here is how it can be done:

  1. The refresh token can be the exactly same JWT as the access-token: custom JSON encrypted and base64 encoded. The result string can be just duplicated. If the access-token contains a lot of data (for example roles), the refresh token may be different as it needs only the user id. Both access and refresh tokens don't have any expiry hardcoded inside.
  2. They are both stored in https_only cookies but the expiration time for the access-token cookie is 2 min and for the refresh-token cookie is 30 min.
  3. Refresh-token stored in DB (User table) and can be easily revoked/invalidated by deleting from DB.
  4. Server looks for access-token in request: if presents and valid (can be decrypted) - OK process request;
  5. if the access-token does not present (cookie expired), the server looks for a refresh-token: if presents - validate by comparing it to one stored in DB and generate a new access-token (based on information from the refresh-token and DB) and process the request.
  6. If the refresh-token is not there, then the server looks for one of the following: username-password pair, third-party identity provider token (Google, Facebook, etc.), third-party identity management system token (Cognito, Okta, JumpCloud). If any are found: process the request and generate new access and refresh tokens.
  7. If nothing is found, then the server sends an authentication error and forces the client to relogin the user.
Yuriy N.
  • 4,936
  • 2
  • 38
  • 31
  • > `and for the refresh-token cookie is 30 min` > `If the refresh-token is not there` - this means if user did not interact with backend for >30 minutes he is forced to re-enter password. To me this is discouraging UX. To countermeasure this set the refresh-token life time to 10 days. But now due to long life it can be stolen and used by an attacker despide the access-token 2 minutes life time. So no need to bother with refresh-token, just set access-token life time to 10 days (or 30 days). Anyway either UX or security would suffer. So I would not accept this answer as an applicable solution – Valentine Shi Nov 30 '22 at 06:31
  • @ValentineShi Yes, 30 minutes can be too short for regular apps. The number comes from Java Enterprise (banks) best practices. You can make it 30 days or whatever. But you don't right about refresh tokens being redundant. As the refresh token is stored in DB (you probably missed that part) it can be invalidated at any time, for example, for a banned user. And if you remove the refresh token from the scheme and store an access token in DB then you need to check it with every request. Refresh token actually saves your app from checking access tokens against DB with every request. – Yuriy N. Nov 30 '22 at 16:34
  • The desired result of the entire thing is improved UX (less frequent user logins). Most of human user API requests hit the DB for data. No issue each request spends 2 more ms for a trip to user sessions table with a token (does not matter whether access- or refrech-token is used). Refresh-token does not add securiy, in either approach either long-lived token can be stolen and used to pretend being a user. Then I see no use for a refresh token. – Valentine Shi Nov 30 '22 at 17:01
  • 2 ms is an obvious and intentional exaggeration ))). Your DB server is ideally separated from an application server. So it will be more, let's say 50ms. Then let's look at Ebay product page - it sends about 20 XHRequests. Ebay pages are accessed several thousand times per second. So we speak here about dozens of thousands of DB hits per second that can be avoided by using refresh token. – Yuriy N. Nov 30 '22 at 17:41
  • Also, JWT authentication is not about improving UX, it is about stateless servers and scalability. Not every app can benefit from using them. And the whole thing is being rightfully criticized and many good developers decided not to use JWT for authentication as do you. But, my answer is about how TO use JWT for authentication, as according to my experience it is just much faster than the traditional Java approach. – Yuriy N. Nov 30 '22 at 18:26
  • The speed withing the same data center between different servers is 0.5 ms. 2 ms trip to DB is pretty long though, one could configure DB to be loaded in memory, Our job is always about UX, and auth. besides, is about security. Returning to the question - I clearly showed refresh-tokens are redundant - neither add to UX nor to security in the scheme you described. PS: [Latency Numbers](https://colin-scott.github.io/personal_website/research/interactive_latency.html) – Valentine Shi Dec 01 '22 at 06:03
  • It seems to be that you are right about latency, at least in the vacuum-spherical sense. But your initial comment and all consequent are misleading. The whole point of JWT is to store critical info inside tokens and make the server stateless. The authentication scheme that you propose is completely valid but it does not require JWT at all. You should have commented on the question with the objective to use JWT for authentication. – Yuriy N. Dec 01 '22 at 11:12