How to secure a refresh token?

19,171

Solution 1

I'm not an expert on all the intricacies of the different tokens, their mechanisms, and their storage best practices (so refer to other articles/experts on the subject - Tim Hardy poses an excellent and strong counterargument to my findings in the comments below) but it seems like a bad idea to use refresh tokens with browser based apps. Refresh tokens can be stored securely on phones/other devices. You could potentially use an http only cookie in a browser or store the token in memory (see more below) but again, I'm not certain on the security of this approach (I'm not saying its NOT secure, I'm saying I don't know how secure)


an improvement can be made to this answer. while I personally dont know the full extent of risks involved with storing access/refresh tokens in browser storage, a short lived (< 10 min) access token in localstorage + an in-memory refresh token can vastly improve the user experience. This way, as long as the user doesn't do a hard refresh, or navigate away from the website/webapp, they will be logged in indefinitely.

Solution 2

There is a good document OAuth 2.0 for Browser-Based Apps which discusses best practices for these applications.

I would choose between keeping tokens on client or server. Mixing it (keeping refresh tokens on your server and access tokens in browser), you create your own protocol with its own vulnerabilities.

If the browser application needs the access token just to access its backend, you could consider using your backend as an OAuth2 client (which receives an auth code), get the user's indentity, issue a cookie which will maintain a session between the browser and the backend. It's much easier than exchanging, refreshing and validating OAuth2 tokens.

If you really want to keep your browser application as an OAuth2 client which receives tokens, you should use PKCE extension (so the auth code kept in network caches and browser history cannot be used to get tokens) and get a new refresh token with each new access token - take a look at the chapter about refresh tokens:

Authorization servers SHOULD NOT issue refresh tokens to browser-based applications.

If an authorization server does choose to issue refresh tokens to browser-based applications, then it MUST issue a new refresh token with every access token refresh response. Doing this mitigates the risk of a leaked refresh token, as a leaked refresh token can be detected if both the attacker and the legitimate client attempt to use the same refresh token.

Your browser application can keep its tokens in sessionStorage to survive page reloads.

Solution 3

You shouldn't store the token on the server. A client authenticates and gets the token. You store the token in the browsers in a cookie or localStorage. Each request is authorised with the token. If you send it over an unencrypted channel without ssl it is open to being intercepted. A hacker getting the token does allow them to impersonate the user. Expired tokens should not allow reauthentication without reentering the users credentials again. Expired tokens should be ignored.

Solution 4

The access token & refresh token are meant to be used is as follows:

  1. Generate expiring access & refresh token on user login and send to front-end app ( Android, IOS, Web App).
  2. Front-end App securely stored refresh token in its db.
  3. Front-end App sends access token with every request and JWT verifies it without hitting database.
  4. Authentication works for defined time of access token.
  5. When it expires, Front-end app sends refresh token to your server, additionally you verify it using JWT and also check it in database for equality.
  6. Server generates new access token and so on.

PS: Whole communication should take place over HTTPS.

I have my implementation based on above logic with access token expiring every 30 mins and refresh token with a year validity.

Also the thing with verification of refresh token with database is that you have control over user login process and you can limit the number of devices able to use your apps with same account.

You simply need to update refresh token on server whenever user sends a login request again.

Share:
19,171
Abdul Ahmad
Author by

Abdul Ahmad

Stuff that I do: node - express - react - angular - sequelize react native ruby on rails asp.net-mvc - c# - java - python design: html - css - sass iOS

Updated on June 11, 2022

Comments

  • Abdul Ahmad
    Abdul Ahmad almost 2 years

    I'm using JWTs for authenticating users for my app. When a user logs in they are given an access token and a refresh token. To keep the refresh token safe, I don't store it on the client-side, but save it on the back-end with their account so it's not easy to access. I'm confused about the security of refresh tokens though, here's the logic that I'm understanding when I read online resources on how to use refresh tokens:

    1. authenticate
    2. store access token + refresh token somewhere (in my case, access token on the front-end and refresh token on the back-end)
    3. when performing an api request, validate the access token on the api side
    4. if the access token is expired, use the refresh token to generate a new access token + new refresh token, send access token back to client
    5. store tokens as before... and repeat

    The security issue I'm worried about is if someone else (hacker) got a hold of the access token and they send a request to the api with it, if the token is expired the api will use the refresh token to get a new access token + new refresh token and return at least the access token to the hacker.

    I read this article about 5-6 times and I read this article a few times, as well as some other articles on the subject, they all say something along the lines of

    make sure to store the refresh token securely because it's long lived, the access_token is short lived so not as big of a deal

    But according to the flow I described above, it doesn't matter if the access token is short lived, the refresh token will be used to get a new access token and have access forever.

    Is there something I'm missing? How would the api know who is sending the request if a hacker got a hold of the expired access token? it will still send a new one using the refresh token. Am I supposed to somehow validate who is sending the request?


    UPDATE

    So I do understand that when a new access token is requested, I need to send over the refresh token, the client ID, and the client secret. The issue I have with that is, like before, the hacker can send a request to my API server, the server gets the hijacked access token from the hacker, it will see that it's expired, so it will send the refresh token, along with the clientID/client secret (which are stored as environment variables) to the Auth API and get back a new access token / refresh token, which brings us back to the same issue.


    UPDATE 2

    some interesting questions on the subject:

    1. Why Does OAuth v2 Have Both Access and Refresh Tokens?
    2. https://security.stackexchange.com/questions/87119/how-secure-are-expiring-tokens-and-refresh-tokens

    according to the second question and answer, it seems like the refresh token is not a more secure way to maintain access, it's just that it's easier to detect a hacker because auth/refresh tokens keep getting requested and invalidating the other's tokens. The issue with this is this will only happen if 2 users are simultaneously trying to access resources - if only the hacker happens to be active at a given time period, he will have unlimited access to the original users data until the original user tries to use the app and access protected resources

  • Abdul Ahmad
    Abdul Ahmad about 5 years
    well the access token gets stored on the front-end / client-side. The refresh token should not be stored in the browser per the articles I linked
  • Abdul Ahmad
    Abdul Ahmad about 5 years
    I see, and where would I get the client credentials if I don't store them?
  • Abdul Ahmad
    Abdul Ahmad about 5 years
    you mean if i'm storing the refresh token on the client side? ie - in the browser?
  • Abdul Ahmad
    Abdul Ahmad about 5 years
    this is assuming the auth and api server are the same? I still don't understand how this prevents a hacker. Say I got a hold of your access token, i send a request to the api server for data, jwt verifies the token and sees it's expired, api says 'ok, let me get a new access token' so it sends a request for a new access token from the auth server using the refresh token stored in the db. It uses the your user id from the expired access token to get the right refresh token from the db. Or how else does it get the refresh token from the db?
  • Aditya T
    Aditya T about 5 years
    this is assuming the auth and api server are the same? doesn't matter as long as you use same secret key on all servers it won't matter if its same server or not. don't understand how this prevents a hacker? here api is not going to send request for new access token, your app would because app has the refresh token stored securely. If like more detailed explain, I can update my answer accordingly. Also note, Its not that easy to parse data sent over HTTPS.
  • Aditya T
    Aditya T about 5 years
    For someone to get your access token or refresh token they have to physically steal your device and decrypt the database( time consuming ) or misguide you in installing some malware app. Its not that easy & also doing all this requires time, so you can accordingly set token expiry time based how critical is security to you.
  • Abdul Ahmad
    Abdul Ahmad about 5 years
    can you clarify the difference between 'app' and ' api' here? you're saying that the 'api is not going to send request for new access token'. But my client secret key is stored on my server (which is the api) - how can I use the client secret key if it's on my server, but my server is not going to send the request?
  • Aditya T
    Aditya T about 5 years
    The original source of request is responsible for storing the refresh token securely, it can be a Android / IOS app or front-end web app. If request is rejected by access token, that app must send the request to refresh access token.
  • Nabs
    Nabs about 4 years
    I have the exact same question and it seems like there's no agreed upon what should be done with refresh tokens. What if you have both SPA web app and also a native mobile app, how would you implement authentication to serve both platforms?
  • Abdul Ahmad
    Abdul Ahmad about 4 years
    you would use refresh token on mobile app and no refresh token on SPA. at least, that's what i'd do
  • Nabs
    Nabs about 4 years
    As far as I understand, is a refresh token supposed to be on the client side in order to refresh an access token? Otherwise, how do you refresh access tokens? And also it's an invalidating mechanism so what the user's password has been changed then a new refresh token is issued so the old one is no longer valid. I agree that a lot of discussions seem to go in different ways and no real solution is out there. This is an article I found about storing the refresh token on the client side: hasura.io/blog/best-practices-of-using-jwt-with-graphql/…
  • Zmey
    Zmey over 3 years
    @AdityaT So how can front-end web app store refresh token securely given that non-httpOnly cookies or localStorage can be accessed by any JS script on the page, including third-party ones (such as payment provider, analytics scripts etc)?
  • Tim Hardy
    Tim Hardy about 3 years
    I disagree. Refresh tokens can be invalidated on the server, and they can also be used with DeviceIds, kept in httponly cookies. If the request for a new access token comes with a refresh token but not the correct DeviceId, the server can reject it. This is the basic mechanism of many auth systems today. It's why you see the "I see you're logging in from a new device" messages with a lot of systems, and it works very well.
  • Frank
    Frank almost 3 years
    @TimHardy It's also interesting that these mechanisms aren't mentioned in OIDC.
  • Abdul Ahmad
    Abdul Ahmad over 2 years
    @TimHardy thanks for the response, I referenced you in the answer to make sure I provide as much context to other users as possible.
  • ymajoros
    ymajoros almost 2 years
    The section you mention has been changed (and will change further). There is no reason to not issue refresh tokens if either they can be kept safely (e.g. service worker), or if you consider that it will impact applications after a successful XSS, which is the main issue you should focus on.