Why Does OAuth v2 Have Both Access and Refresh Tokens?

SecurityOauth 2.0OauthAccess TokenRefresh Token

Security Problem Overview


Section 4.2 of the draft OAuth 2.0 protocol indicates that an authorization server can return both an access_token (which is used to authenticate oneself with a resource) as well as a refresh_token, which is used purely to create a new access_token:

https://www.rfc-editor.org/rfc/rfc6749#section-4.2

Why have both? Why not just make the access_token last as long as the refresh_token and not have a refresh_token?

Security Solutions


Solution 1 - Security

The link to discussion, provided by Catchdave, has another valid point (original, dead link) made by Dick Hardt, which I believe is worth to be mentioned here in addition to what's been written above:

> My recollection of refresh tokens was for security and revocation. > <...> > > revocation: if the access token is self contained, authorization can be revoked by not issuing new access tokens. A resource does not need to query the authorization server to see if the access token is valid.This simplifies access token validation and makes it easier to scale and support multiple authorization servers. There is a window of time when an access token is valid, but authorization is revoked.

Indeed, in the situation where Resource Server and Authorization Server is the same entity, and where the connection between user and either of them is (usually) equally secure, there is not much sense to keep refresh token separate from the access token.

Although, as mentioned in the quote, another role of refresh tokens is to ensure the access token can be revoked at any time by the User (via the web-interface in their profiles, for example) while keeping the system scalable at the same time.

Generally, tokens can either be random identifiers pointing to the specific record in the Server's database, or they can contain all information in themselves (certainly, this information have to be signed, with MAC, for example).

How the system with long-lived access tokens should work

The server allows the Client to get access to User's data within a pre-defined set of scopes by issuing a token. As we want to keep the token revocable, we must store in the database the token along with the flag "revoked" being set or unset (otherwise, how would you do that with self-contained token?) Database can contain as much as len(users) x len(registered clients) x len(scopes combination) records. Every API request then must hit the database. Although it's quite trivial to make queries to such database performing O(1), the single point of failure itself can have negative impact on the scalability and performance of the system.

How the system with long-lived refresh token and short-lived access token should work

Here we issue two keys: random refresh token with the corresponding record in the database, and signed self-contained access token, containing among others the expiration timestamp field.

As the access token is self-contained, we don't have to hit the database at all to check its validity. All we have to do is to decode the token and to validate the signature and the timestamp.

Nonetheless, we still have to keep the database of refresh tokens, but the number of requests to this database is generally defined by the lifespan of the access token (the longer the lifespan, the lower the access rate).

In order to revoke the access of Client from a particular User, we should mark the corresponding refresh token as "revoked" (or remove it completely) and stop issuing new access tokens. It's obvious though that there is a window during which the refresh token has been revoked, but its access token may still be valid.

Tradeoffs

Refresh tokens partially eliminate the SPoF (Single Point of Failure) of Access Token database, yet they have some obvious drawbacks.

  1. The "window". A timeframe between events "user revokes the access" and "access is guaranteed to be revoked".

  2. The complication of the Client logic.

    without refresh token

    • send API request with access token
    • if access token is invalid, fail and ask user to re-authenticate

    with refresh token

    • send API request with access token
    • If access token is invalid, try to update it using refresh token
    • if refresh request passes, update the access token and re-send the initial API request
    • If refresh request fails, ask user to re-authenticate

I hope this answer does make sense and helps somebody to make more thoughtful decision. I'd like to note also that some well-known OAuth2 providers, including github and foursquare adopt protocol without refresh tokens, and seem happy with that.

Solution 2 - Security

The idea of refresh tokens is that if an access token is compromised, because it is short-lived, the attacker has a limited window in which to abuse it.

Refresh tokens, if compromised, are useless because the attacker requires the client id and secret in addition to the refresh token in order to gain an access token.

Having said that, because every call to both the authorization server and the resource server is done over SSL - including the original client id and secret when they request the access/refresh tokens - I am unsure as to how the access token is any more "compromisable" than the long-lived refresh token and clientid/secret combination.

This of course is different to implementations where you don't control both the authorization and resource servers.

Here is a good thread talking about uses of refresh tokens: OAuth Archives.

A quote from the above, talking about the security purposes of the refresh token:

> Refresh tokens... mitigates the risk of a long-lived access_token leaking (query param in a log file on an insecure resource server, beta or poorly coded resource server app, JS SDK client on a non https site that puts the access_token in a cookie, etc)

Solution 3 - Security

Despite all the great answers above, I as a security master student and programmer who previously worked at eBay when I took a look into buyer protection and fraud, can say to separate access token and refresh token has its best balance between harassing user of frequent username/password input and keeping the authority in hand to revoke access to potential abuse of your service.

Think of a scenario like this. You issue user of an access token of 3600 seconds and refresh token much longer as one day.

  1. The user is a good user, he is at home and gets on/off your website shopping and searching on his iPhone. His IP address doesn't change and have a very low load on your server. Like 3-5 page requests every minute. When his 3600 seconds on the access token is over, he requires a new one with the refresh token. We, on the server side, check his activity history and IP address, think he is a human and behaves himself. We grant him a new access token to continue using our service. The user won't need to enter again the username/password until he has reached one day life-span of refresh token itself.

  2. The user is a careless user. He lives in New York, USA and got his virus program shutdown and was hacked by a hacker in Poland. When the hacker got the access token and refresh token, he tries to impersonate the user and use our service. But after the short-live access token expires, when the hacker tries to refresh the access token, we, on the server, has noticed a dramatic IP change in user behavior history (hey, this guy logins in USA and now refresh access in Poland after just 3600s ???). We terminate the refresh process, invalidate the refresh token itself and prompt to enter username/password again.

  3. The user is a malicious user. He is intended to abuse our service by calling 1000 times our API each minute using a robot. He can well doing so until 3600 seconds later, when he tries to refresh the access token, we noticed his behavior and think he might not be a human. We reject and terminate the refresh process and ask him to enter username/password again. This might potentially break his robot's automatic flow. At least makes him uncomfortable.

You can see the refresh token has acted perfectly when we try to balance our work, user experience and potential risk of a stolen token. Your watch dog on the server side can check more than IP change, frequency of api calls to determine whether the user shall be a good user or not.

Another word is you can also try to limit the damage control of stolen token/abuse of service by implementing on each api call the basic IP watch dog or any other measures. But this is expensive as you have to read and write record about the user and will slow down your server response.

Solution 4 - Security

Neither of these answers get to the core reason refresh tokens exist. Obviously, you can always get a new access-token/refresh-token pair by sending your client credentials to the auth server - that's how you get them in the first place.

So the sole purpose of the refresh token is to limit the use of the client credentials being sent over the wire to the auth service. The shorter the TTL of the access-token, the more often the client credentials will have to be used to obtain a new access-token, and therefore the more opportunities attackers have to compromise the client credentials (although this may be super difficult anyway if asymmetric encryption is being used to send them). So if you have a single-use refresh-token, you can make the TTL of access-tokens arbitrarily small without compromising the client credentials.

Solution 5 - Security

To clear up some confusion you have to understand the roles of the client secret and the user password, which are very different.

The client is an app/website/program/..., backed by a server, that wants to authenticate a user by using a third-party authentication service. The client secret is a (random) string that is known to both this client and the authentication server. Using this secret the client can identify itself with the authentication server, receiving authorization to request access tokens.

To get the initial access token and refresh token, what is required is:

  • The user ID
  • The user password
  • The client ID
  • The client secret

To get a refreshed access token however the client uses the following information:

  • The client ID
  • The client secret
  • The refresh token

This clearly shows the difference: when refreshing, the client receives authorization to refresh access tokens by using its client secret, and can thus re-authenticate the user using the refresh token instead of the user ID + password. This effectively prevents the user from having to re-enter his/her password.

This also shows that losing a refresh token is no problem because the client ID and secret are not known. It also shows that keeping the client ID and client secret secret is vital.

Solution 6 - Security

This answer has been put together by the help of two senior devs (John Brayton and David Jennes).

The main reason to use a refresh token is to reduce the attack surface.

Let's suppose there is no refresh key and let’s go through this example:

A building has 80 doors. All doors are opened with the same key. The key changes every 30 minutes. At the end of the 30 minutes I have to give the old key to the keymaker and get a new key.

If I’m the hacker and get your key, then at the end of the 30 minutes, I’ll courier that to the keymaker and get a new key. I’ll be able to continuously open all doors regardless of the key changing.

Question: During the 30 minutes, how many hacking opportunities did I have against the key? I had 80 hacking opportunities, each time you used the key (think of this as making a network request and passing the access token to identify yourself). So that’s 80X attack surface.

Now let’s go through the same example but this time let’s assume there’s a refresh key.

A building has 80 doors. All doors are opened with the same key. The key changes every 30 minutes. To get a new key, I can’t pass the old access token. I must only pass the refresh key.

If I’m the hacker and get your key, I can use it for 30 minutes, but at the end of the 30 minutes sending it to the keymaker has no value. If I do, then the keymaker would just say "This token is expired. You need to refresh the token." To be able to extend my hack I would have to hack the courier to the keymaker. The courier has a distinct key (think of this as a refresh token).

Question: During the 30 minutes, how many hacking opportunities did I have against the refresh key? 80? No. I only had 1 hacking opportunity. During the time the courier communicates with the keymaker. So that’s 1X attack surface. I did have 80 hacking opportunities against the key, but they are no good after 30 minutes.


A server would verify an access token based on credentials and signing of (typically) a JWT.

An access token leaking is bad, but once it expires it is no longer useful to an attacker. A refresh token leaking is far worse, but presumably it is less likely. (I think there is room to question whether the likelihood of a refresh token leaking is much lower than that of an access token leaking, but that’s the idea.)

Point is that the access token is added to every request you make, whereas a refresh token is only used during the refresh flow So less chance of a MITM seeing the token

Frequency helps an attacker. Heartbleed-like potential security flaws in SSL, potential security flaws in the client, and potential security flaws in the server all make leaking possible.

In addition, if the authorization server is separate from the application server processing other client requests then that application server will never see refresh tokens. It will only see access tokens that will not live for much longer.

Compartmentalization is good for security.

Last but not least refresh tokens can get rotated. Meaning 'a new refresh token is returned each time the client makes a request to exchange a refresh token for a new access token.'. As refresh tokens are continually exchanged and invalidated, the threat is reduced. To give you an example: Tokens are usually expired after a TTL usually an hour.

Refresh tokens not always, but often are revoked upon usage and a new one issued. Meaning if you ever have a network failure, when you're retrieving the new refresh token, then the next time you send that refresh token, it's considered revoked and you have to sign in.

For more on rotation see here and here

Summary

  • Reducing Frequency
  • Compartmentalization
  • Rotation (quicker invalidation) and more granular management (expiration time or number of requests made) of tokens.

All help to to mitigate threats

For another take on this see this awesome answer


What refresh token is NOT about?

The ability to update/revoke access level through refresh tokens is a byproduct of choosing to use refresh tokens, otherwise a standalone access token could be revoked or have its access level modified when it expires and users gets a new token

Solution 7 - Security

This answer is from Justin Richer via the OAuth 2 standard body email list. This is posted with his permission.


The lifetime of a refresh token is up to the (AS) authorization server — they can expire, be revoked, etc. The difference between a refresh token and an access token is the audience: the refresh token only goes back to the authorization server, the access token goes to the (RS) resource server.

Also, just getting an access token doesn’t mean the user’s logged in. In fact, the user might not even be there anymore, which is actually the intended use case of the refresh token. Refreshing the access token will give you access to an API on the user’s behalf, it will not tell you if the user’s there.

OpenID Connect doesn’t just give you user information from an access token, it also gives you an ID token. This is a separate piece of data that’s directed at the client itself, not the AS or the RS. In OIDC, you should only consider someone actually “logged in” by the protocol if you can get a fresh ID token. Refreshing it is not likely to be enough.

For more information please read http://oauth.net/articles/authentication/

Solution 8 - Security

Clients can be compromised in many ways. For example a cell phone can be cloned. Having an access token expire means that the client is forced to re-authenticate to the authorization server. During the re-authentication, the authorization server can check other characteristics (IOW perform adaptive access management).

Refresh tokens allow for a client only re-authentication, where as re-authorize forces a dialog with the user which many have indicated they would rather not do.

Refresh tokens fit in essentially in the same place where normal web sites might choose to periodically re-authenticate users after an hour or so (e.g. banking site). It isn't highly used at present since most social web sites don't re-authenticate web users, so why would they re-authenticate a client?

Solution 9 - Security

To further simplify B T's answer: Use refresh tokens when you don't typically want the user to have to type in credentials again, but still want the power to be able to revoke the permissions (by revoking the refresh token)

You cannot revoke an access token, only a refresh token.

Solution 10 - Security

> Why not just make the access_token last as long as the refresh_token > and not have a refresh_token?

In addition to great answers other people have provided, there is another reason why we would use refresh tokens and it's to do with claims.

Each token contains claims which can include anything from the user's name, their roles, or the provider which created the claim. As a token is refreshed, these claims are updated.

If we refresh the tokens more often, we are obviously putting more strain on our identity services; however, we are getting more accurate and up-to-date claims.

Solution 11 - Security

Assume you make the access_token last very long, and don't have refresh_token, so in one day, hacker get this access_token and he can access all protected resources!

But if you have refresh_token, the access_token's live time is short, so the hacker is hard to hack your access_token because it will be invalid after short period of time. Access_token can only be retrieved back by using not only refresh_token but also by client_id and client_secret, which hacker doesn't have.

Solution 12 - Security

While refresh token is retained by the Authorization server. Access token are self-contained so resource server can verify it without storing it which saves the effort of retrieval in case of validation. Another point missing in discussion is from rfc6749#page-55

> "For example, the authorization server could employ refresh token > rotation in which a new refresh token is issued with every access > token refresh response.The previous refresh token is invalidated but > retained by the authorization server. If a refresh token is > compromised and subsequently used by both the attacker and the > legitimate client, one of them will present an invalidated refresh > token, which will inform the authorization server of the breach."

I think the whole point of using refresh token is that even if attacker somehow manages to get refresh token, client ID and secret combination. With subsequent calls to get new access token from attacker can be tracked in case if every request for refresh result in new access token and refresh token.

Solution 13 - Security

Its all about scaling and keeping your resource server stateless.

  • Your server / resource server
    • Server is stateless, meaning does not check any storage to respond very fast. Does this by using a public key to verify the signature of the token.

    • Checks access_token on every single request.

    • By only checking the signature and expiration date of access_token, response is very fast and allows scaling.

    • access_token should have short expiration time (a few minutes), since there is no way to revoke it, if it gets leaked damage is limited.

  • Authentication server / OAuth server
    • Server is not stateless, but its ok because requests are much fewer.
    • Checks refresh_token only when access_token is expired. (every 2 minutes for example)
    • Request rate is much lower than resource server.
    • Stores the refresh token in a DB and can revoke it.
    • refresh_token can have long expiration time (few weeks/months), if it gets leaked there is a way to revoke it.

There is a important note though, the authentication server has much fewer requests so can handle load, however there can be a storage issue since it has to store all refresh_tokens, and if users increase dramatically this could become a problem.

Solution 14 - Security

Let's consider a system where each user is linked to one or more roles and each role is linked to one or more access privileges. This information can be cached for better API performance. But then, there may be changes in the user and role configurations (for e.g. new access may be granted or current access may be revoked) and these should be reflected in the cache.

We can use access and refresh tokens for such purpose. When an API is invoked with access token, the resource server checks the cache for access rights. IF there is any new access grants, it is not reflected immediately. Once the access token expires (say in 30 minutes) and the client uses the refresh token to generate a new access token, the cache can be updated with the updated user access right information from the DB.

In other words, we can move the expensive operations from every API call using access tokens to the event of access token generation using refresh token.

Solution 15 - Security

Refresh tokens and Access tokens are mere terminologies.

This little analogy can help solidify the rationale behind using Access Tokens and Refresh Tokens:

Suppose Alice sends a cheque to Bob via post, which can be encashed within 1 hour (hypothetical) from the time of issue, else the bank will not honor it. But Alice has also included a note in the post meant for the bank, asking the bank to accept and encash the cheque in case it gets a bit delayed (within a stipulated range)

When Bob receives this cheque, he will himself discard this cheque, if he sees this tampered (token tampering). If not, he can take it to the bank to encash it. Here, when the bank notices that the time of issue has surpassed the 1-hour time limit, but sees a signed note from Alice asking the bank to encash in case of stipulated delay within an acceptable range.

Upon seeing this note, the bank tries to verify the signed message and checks if Alice still has the right permissions. If yes, the bank encashes the cheque. Bob can now acknowledge this back to Alice.

Although not terribly accurate, this analogy can you help notice the different parts involved in processing the transaction:

  • Alice (Sender - Client)
  • Bob (Receiver - Resource Server)
  • Bank (Authorization Server)
  • Verification Process (Database Access)
  • Cheque (Access Token)
  • Note (Refresh Token)

Mainly, we want to reduce the number of API calls to the Auth Server, and eventually to the Database, in order to optimize scalability. And we need to do this with the right balance between convenience and security.

Note: It's certainly more common to have the Auth server responding to the requests earlier than the resource server in the chain.

Solution 16 - Security

> First, the client authenticates with the authorization server by giving the authorization grant. > >Then, the client requests the resource server for the protected resource by giving the access token. > >The resource server validates the access token and provides the protected resource. > >The client makes the protected resource request to the resource server by granting the access token, where the resource server validates it and serves the request, if valid. This step keeps on repeating until the access token expires. > >If the access token expires, the client authenticates with the authorization server and requests for a new access token by providing refresh token. If the access token is invalid, the resource server sends back the invalid token error response to the client. > >The client authenticates with the authorization server by granting the refresh token. > >The authorization server then validates the refresh token by authenticating the client and issues a new access token, if it is valid.

Solution 17 - Security

From what I understand, refresh tokens are there just for performance and cost savings if you need to be able to revoke access.

Eg 1: Do not implement refresh tokens; implement just long-lived access tokens: You need to be able to revoke access tokens if the user is abusing the service (eg: not paying the subscription) => You would need to check the validity of the access token on every API call that requires an access token and this will be slow because it needs a DB look-up (caching can help, but that's more complexity).

Eg 2: Implement refresh tokens and short-lived access tokens: You need to be able to revoke access tokens if the user is abusing the service (eg: not paying the subscription) => The Short-lived access tokens will expire after a short white (eg. 1hr) and the user will need to get a new access token, so we don't need validation on every API call that requires an access token. You just need to validate the user when generating the access token from the refresh token. For a bad user, you can log out the user if an access token cannot be generated. When the user tries to log back in, the validation will run again and returns an error.

Solution 18 - Security

Since the refresh and access tokens are terms loaded with a lot of semantics a terminology shift could help?

  • Revokable Tokens - tokens that must be checked with authorization server
    • could be chained (see RTR - refresh token rotation)
    • can be used to create NonRevokable Tokens, but can also be used directly (when volumes are small and the check doesn't become a burden)
    • can be long lived but that depends on how often the user must be bothered with credentials (username/password) to get a new one
    • can be invalidated on RTR or any other suspect behavior
  • NonRevokable Tokens - tokens that are self contained and do not need to be checked with authorization server
    • are useful for big data, distributed servers/api calls to scale horizontally
    • should be short lived (since are non revokable)

In 2020 it become accepted that refresh token can also exist in the browser (initially was offered for backend systems) - see https://pragmaticwebsecurity.com/articles/oauthoidc/refresh-token-protection-implications. Because of this the focus was switched from the "refreshability" (how would a backend in absence of a user prolong the access to an api) to "revokability".

So, to me it looks safer to read the refresh tokens as Revokable Tokens and access tokens as Non-Revokable Tokens (maybe Fast Expiring Non Revokable Tokens) .

As a side note about good practice in 2021 a system can always start with revokable tokens and move to non-revokable when the pressure on authorization server increases.

Solution 19 - Security

There are two important points we need to understand in order to understand the answer to this question.

  1. The first point is, sometimes a user's access token might get stolen without the user knowing anything about it. Since user is unaware of the attack, they will not be able to inform us manually. Then, there will be a huge difference between e.g. 15 minutes and a whole day, with regards to the amount of time(opportunity) we have given the attacker to accomplish its attacks. So this is the reason we need to "refresh" access tokens ourselves every "short period of time" (e.g. every 15 minutes), we don't want to put off doing this for a long time (e.g. a whole day). So what the OP has said in the question is obviously not an option (stretching access token's expiry time as long as refresh token's).

So we have at least these two options left for us:

  • Asking each user to re-enter their credentials every short period of time in order to give them fresh access tokens. But obviously, this is not a popular option as it would be bothering to the users.

  • Using a refresh token. Read the second point below in order to understand how it works (the logic behind it).

  1. The second point to understand is, because we have separated access token from refresh token, now refresh token can be sent in a "different way", so we can send it in a way that it will be inaccessible to attackers' JavaScript (client side code in general), e.g., using the httpOnly tags:

> An HttpOnly Cookie is a tag added to a browser cookie that prevents client-side scripts from accessing data. source

> Using the HttpOnly flag when generating a cookie helps mitigate the risk of client side script accessing the protected cookie. HttpOnly cookies were first implemented in 2002 by Microsoft Internet Explorer developers for Internet Explorer 6 SP1. source (Thank you IE!)

So while attackers might be able to still steal access tokens (it is highly recommended though to keep them in RAM, rather than in vulnerable places like localstorage), they won't be able to steal refresh tokens. So, if an attacker steals one's access token, they will only have a short period of time for abusing it (15 minutes? A whole lot better than a whole day!), and then as soon as it expires, they will not have a chance to get a fresh one on their own as well.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questiondave mankoffView Question on Stackoverflow
Solution 1 - SecurityRoman ImankulovView Answer on Stackoverflow
Solution 2 - SecuritycatchdaveView Answer on Stackoverflow
Solution 3 - SecuritylaalaguerView Answer on Stackoverflow
Solution 4 - SecurityB TView Answer on Stackoverflow
Solution 5 - SecurityAdversusView Answer on Stackoverflow
Solution 6 - SecuritymfaaniView Answer on Stackoverflow
Solution 7 - SecurityManicodeView Answer on Stackoverflow
Solution 8 - SecurityPhilView Answer on Stackoverflow
Solution 9 - SecuritybitcoderView Answer on Stackoverflow
Solution 10 - SecurityheymegaView Answer on Stackoverflow
Solution 11 - SecurityTạ Anh TúView Answer on Stackoverflow
Solution 12 - SecurityKramingView Answer on Stackoverflow
Solution 13 - SecurityBernard WiesnerView Answer on Stackoverflow
Solution 14 - SecuritySaptarshi BasuView Answer on Stackoverflow
Solution 15 - SecurityKJ SudarshanView Answer on Stackoverflow
Solution 16 - Securityuser8288060View Answer on Stackoverflow
Solution 17 - SecurityAnusha DharmasenaView Answer on Stackoverflow
Solution 18 - SecurityraisercostinView Answer on Stackoverflow
Solution 19 - SecurityaderchoxView Answer on Stackoverflow