How to secure a refresh token?
Asked Answered
B

7

26

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

Benitobenjamen answered 3/4, 2019 at 2:56 Comment(0)
B
21

TL;DR (an option for improving user experience)

  1. Store a short lived access token in-memory OR localstorage.
  2. Store refresh token in-memory (only)

now as long as the user doesnt do a hard refresh or navigate away from your site, they can be logged in forever.


General thoughts

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)


Improving user experience

while (again) I personally dont know the full extent of risks involved with storing access/refresh tokens in browser storage (besides XSS attacks), you have a couple relatively safe options here.

Either store a long-lived access token ONLY in-memory, or use a combination of a short-lived access token (in-memory or localstorage is safe too probably since its short-lived) plus a refresh token ONLY in-memory. This approach allows the user to be logged in forever as long as they dont refresh the page or navigate away.

Benitobenjamen answered 12/7, 2019 at 20:25 Comment(6)
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?Concinnity
you would use refresh token on mobile app and no refresh token on SPA. at least, that's what i'd doBenitobenjamen
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/…Concinnity
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.Loppy
@TimHardy It's also interesting that these mechanisms aren't mentioned in OIDC.Pepita
@TimHardy thanks for the response, I referenced you in the answer to make sure I provide as much context to other users as possible.Benitobenjamen
B
5

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.

Began answered 5/4, 2019 at 9:29 Comment(1)
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.Dogtrot
O
3

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.

Overstuff answered 3/4, 2019 at 4:16 Comment(6)
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?Benitobenjamen
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.Overstuff
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.Overstuff
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?Benitobenjamen
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.Overstuff
@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)?Pavier
B
1

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.

Brunhilda answered 3/4, 2019 at 3:12 Comment(1)
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 linkedBenitobenjamen
B
0

In the second article you linked it is sayed that to refresh token you have to post the refresh token and client_id and client_secret so basically you re-authenticate user when you refresh the access token.

To use the refresh token, make a POST request to the service’s token endpoint with grant_type=refresh_token, and include the refresh token as well as the client credentials.

Bajaj answered 3/4, 2019 at 3:12 Comment(1)
I see, and where would I get the client credentials if I don't store them?Benitobenjamen
H
0

If you are storing the refresh token on the server, your server should include a secure session cookie in the authentication response to identify the user. You can prevent attackers from extracting secure session cookies by setting the cookies with the HttpOnly flag.

https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#Secure_and_HttpOnly_cookies

The cookie wouldn't be a refresh token. It would be some other kind of session cookie. One application flow in which a refresh token is not returned to the user is a single-page application flow.

https://auth0.com/docs/flows/concepts/single-page-login-flow

In this flow, token refreshing is done through "Silent Authentication."

https://auth0.com/docs/api-auth/tutorials/silent-authentication#initiate-a-silent-authentication-request

A successful authentication response if the user already has a valid session in Auth0 and no consent or other prompts are needed.

So we need to maintain a session by storing some user identifier.

Happ answered 3/4, 2019 at 3:27 Comment(1)
you mean if i'm storing the refresh token on the client side? ie - in the browser?Benitobenjamen
L
0

How secure is a refresh token, depends upon how the authorization server has implemented the policy of identifying the client, regeneration of refresh tokens and invalidation of refresh tokens.

According to a safe policy(for a web client), a refresh token can be invalidated at the time of a request for a new access token, and a new refresh token is also generated by the authorization server and returned to the client. https://www.rfc-editor.org/rfc/rfc6749#section-10.4

From the question I get a sense, that Authorization server and Resource server are treated as one, but its not the case. Authorization server is the one which generates access token and/or refresh token, whereas resource server is the one, which has the resources, a client tries to gain access to. Further, a refresh token, is presented only to an authorization server to get new access token, whereas an access token is presented to resource server by the client to gain access to protected resource(s).

Lalalalage answered 27/8, 2020 at 14:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.