32

If we consider that having a valid refresh token allows you to get a valid access token, then the two can be considered to have the same 'informational value' right?

Then why are they treated differently in terms of security aspects like expiration time?

It is often said that it's best practice to give access tokens shorter lifetimes and rotate them often while in the same time allowing refresh tokens to last longer. A typical example would be to have an access JWT last a week while letting a refresh token expire after 30 days.

But if both tokens are equally valuable to an attacker, I don't understand the different treatment they receive from a security standpoint unless we look at this topic within a context of massive security flaws. If we for example argue that there could be a critical security leak within the https protocol, then sure, an attacker could probably steal an access token more easily since it's exchanged more often between a client and a server. But practically, this shouldn't be the case when https is used and the access jwt is stored securely as an HttpOnly cookie.

2
  • Related: security.stackexchange.com/questions/274267/…
    – mti2935
    Commented Mar 9 at 15:41
  • 1
    Please be more clear in what scenario you are using these tokens. For example in case of OIDC the access token is just used to prove successful authentication against the relying party. The refresh token instead is used to grant a new access token by the OIDC provider, which then can check if the conditions still exist and might deny issuing a new access token. Please check also the many similar questions on this site. Commented Mar 9 at 16:36

5 Answers 5

35

You seem to be confused about the relationship between refresh tokens and JWTs (which are frequently used as access tokens), which is reflected in the fundamental differences between how refresh and access tokens are used. I'll explain that below, and the security considerations of the relationship and uses, in a way that will hopefully resolve your confusion.


The term "access token" frequently refers to a JWT (JSON Web Token). In OAuth this is practically mandatory, though JWTs can be (and frequently are) used outside of OAuth as well. JWTs are stateless; the server has no idea what JWTs exist at any point in time, who they were issued to, when they'll expire, or what access they authorize. Once a JWT is issued, the server forgets it entirely (beyond hopefully noting the event in an audit log).

Stateless tokens have a big advantage in scalability: the server neither needs to store state for each user within itself (which runs into problems with large user counts and also makes load-balancing and failover more difficult) nor retrieve session data from an external data source like a database (which increases request/response latency and load on the DB server). Instead, the token can store everything that the server needs to know about the user, with no lookups required. However, stateless tokens also have a huge disadvantage in security: there is no good way to terminate them ahead of their scheduled expiration, because the server has no idea which tokens are valid. This is a problem for users (the user can't revoke access to potentially-stolen tokens) and for administrators (the admin can't end a logged-in user's access prematurely if e.g. they leave the company or break some rule and need to be banned... or if their token is suspected to be stolen).

The usual fix to that problem is to make the stateless tokens very short-lived. Specifically, a matter of minutes is typical (a week is absolutely FAR too long; even an hour is pushing it). The idea being that any given stateless token will expire so quickly, that even if it is stolen, misused, or granted to somebody whose access must be revoked, the window of opportunity for harm is very short. However, this would be a terrible user experience if it meant you needed to log in (or even bounce off an authorization server / SSO provider) every few minutes. To solve this problem, we introduce refresh tokens.

Refresh tokens are, in a sense, a return to the classic session token. Refresh tokens are generally opaque high-entropy blobs; their contents mean nothing, but can be looked up in a database somewhere. Because the refresh token needs to be stored in the backend (typically in a DB), it's not stateless. However, since it's only needed once every few minutes per active user, it adds minimal load to the DB server and basically no delay (none at all for continuously-active sessions, if they're built right) to normal user actions. One needn't even transmit the refresh token except when receiving it from the server, or the client exchanges it for a new stateless token (i.e. access token). The access token is used for all other requests. Meanwhile, the refresh token can sit on the client and the database for as long as you want a single authentication to last - which could potentially be years (I think StackExchange uses 6 months since last activity). If either the user or an admin want to change or revoke existing sessions, they do this by removing the refresh token(s) or modifying their associated account. Within minutes the stateless access token(s) will expire, and the client will either be refreshed with a new access token that has different privileges, or with the notice that their refresh token is invalid and they need to sign in again (or their account is locked, or whatever).


Hopefully this helps you understand the differences in best practice regarding lifetimes of the two token types. Refresh tokens are designed to be long-lived but must be revoked at need. Access tokens are designed to be short-lived, because they can't be revoked (in most cases). As for the security usefulness of each token, it's a little complicated. Access tokens are more valuable in the very short term - they can't be revoked easily, directly provide authorization, and don't even require checking an auth server - but they expire so fast they're difficult to mount an effective attack around. Refresh tokens are much better for persistent access... assuming nobody notices and revokes them, which is quite easy.

15
  • 2
    Very helpful write-up, thank you - if I try to sum it up in regard to my initial question of "why are access and refresh treated differently when they provide the same value to an attacker", then precisely because of 1. their different expiration times (in your example it becomes much clearer with the 3 min access expiration) and 2. one being stateful hence revocable and the other not, they in fact do not provide the same value to an attacker at all. In fact, a refresh token offers a much higher value but due to it being stateful we can build protective mechanisms around it I suppose
    – Javiliano
    Commented Mar 10 at 16:11
  • 2
    "differences between refresh tokens and JWTs" sounds wrong. Refresh tokens can be in the form of a JWT as well.
    – Bergi
    Commented Mar 10 at 23:49
  • 1
    Also, they don't market it this way, but Auth0 is a toy product for toy (or ignorant) companies that don't need / can't bother with real security. Its checking-boxes approach to feature selection and flawed implementations - which belie a stunning lack of understanding as to what those features are even for - make me wonder if they employ any real security engineers at all, or at least whether they listen to them. I had some hope when Okta bought them, but at this point I just don't respect Okta as much as I used to.
    – CBHacking
    Commented Mar 11 at 1:26
  • 1
    @Bergi I mean, technically speaking you're right, because "JWT" is a token format and "refresh" is a token purpose, and you could use the former for the latter. It'd be stupid, but you could do it. Refresh tokens MUST be revocable, though, and by the time you've jumped through the hoops necessary to make JWTs revocable, you'd usually have been much better off with just a high-entropy token, possibly in place of both the access and refresh tokens. Refresh tokens exist as a work-around to the irrevocability of JWTs.
    – CBHacking
    Commented Mar 11 at 1:32
  • 2
    @CBHacking A non-opaque refresh token allows you to minimise storage, and thus lookup times, by only recording revocations, not all issued tokens. For instance, you can store the record "user X logged out at 13:45", and immediately stop trusting all refresh tokens stored for that user before that time, rather than identifying every unexpired token issued to them. If the received token doesn't match a revocation, you can use its content to authenticate the user, but look up live authorisation information for the access token that has been requested.
    – IMSoP
    Commented Mar 11 at 17:52
23

Access tokens and refresh tokens are not equally valuable for an attacker.

An access token tells the resource server that the client is authorized to access a protected resource. As long as the access token hasn't expired, the server generally grants access to the resource immediately without any further checks. This makes access tokens extremely valuable and leads to the recommendation that access tokens should be short-lived.

Refresh tokens are sent to the authorization server. Contrary to your assumption, the authorization server doesn't just hand out an access token as soon as it receives the token. Instead, it can -- and even must -- perform additional checks. For example, if the authorization server has a chance to authenticate the client, then it must check whether the refresh token actually belongs to the client. Alternatively, if the client cannot be authenticated, then the authorization server is expected to perform basic checks to prevent token abuse. For example, it may save refresh tokens which have already been used and detect reuse attempts (which indicate an attack). The authorization server can also decide to narrow the scope of the new access token by shortening the lifetime or reducing the permissions.

So refresh tokens are less valuable for an attacker. They cannot simply be exchanged for an access token to immediately get access to a protected resource. Instead, the authorization server will first perform checks to detect attacks, and it may also reduce access to the resource.

6
  • 3
    Thank you for the detailed response and clarification. I was indeed under the assumption that servers simply hand out new access tokens when presented with a valid refresh token. After researching more, it seems like there's a distinction between confidential clients and public clients. Confidential clients that are able to store additional secrets securely can identify themselves via those alongside the refresh token, but I'm still confused as to how a public client like a SPA would manage to id itself in a way that's not replicable for an attacker (who's successfully stolen a refresh token)
    – Javiliano
    Commented Mar 9 at 19:56
  • You generally don’t give untrustworthy clients like a SPA a long lasting refresh token. Typically you only Sheba short time to refresh the token (like a few minutes or so). Just to keep logged in users logged in. (With really short token times for access tokens too.).
    – LvB
    Commented Mar 9 at 22:01
  • 4
    There are two recommended approaches for single-page applications: Backend-for-Frontend (BFF) and Token-Mediating Backend. Both completely hide the refresh token from the client and take extra measures to prevent cross-site scripting, so an attacker cannot simply steal tokens.
    – Ja1024
    Commented Mar 9 at 23:05
  • 2
    Autocarrots is weird sometimes. That’s supposed to be “store a”…
    – LvB
    Commented Mar 11 at 3:38
  • 1
    Am I right in thinking that refresh tokens are related to authentication and access tokens are part of authorization? Which means if the authorization settings for an user are changed, then that user can continue to use a refresh token to not have to re-authenticate but should get new access tokens to make sure resources grant/deny based on the new authorization settings, right? Just thinking about important differences between the tokens that are not related to attacks, just day-to-day use. Commented Mar 11 at 14:25
5
  • An authorization token's purpose is for the backend to, when presented with it, be able to immediately give the correct access for that particular user's action. For tokens that are merely opaque session tokens where all systems still perform calls to the authorization system for each and every action, its lifetime is not as critical. However, if the token is a self-contained token like a JWT, where no calls to the authorization system will be made, it needs to be short lived, to prevent a user that has their permissions changed to maintain their old level of access for a long duration. It also offers a layer of protection from theft of authentication tokens, as the window that an attacker has to use the token to perform actions and go mostly undetected is now short. Its lifetime should reflect the trade off of how long the system is willing to provide access to a user after their permissions were changed / load on the authorization system caused by refresh calls / degraded performance due to token refreshes.
  • In contrast, the refresh token's only purpose is to be sent to the authorization system, where the user's state will be – as the name implies – refreshed, therefore its lifetime should only be dictated by for how long do you want a user that has not interacted with the system to still stay logged in (assuming the system extends refresh tokens during normal activity) or, how long you want a user to reauthenticate again next, period (assuming the system does NOT extend refresh tokens).
  • Like it was mentioned in other answers, the call to exchange a refresh token for an access token, since it is only made every few minutes or hours, can involve a much larger check – it can make device checks, geolocation checks, etc. It can even return a new refresh token alongside the new access token and invalidate the old refresh token, meaning if a refresh token gets stolen and used, the original user will be logged out and potentially notice something is wrong.
  • That being said, do not forget a refresh token is intrinsically tied to how long a user has until they have to reauthenticate themselves. A refresh token that expires too often means the user gets logged out all the time. In particular, a mobile app that insists on repeatedly logging a user out is the same as no app at all – it will get uninstalled by the end user. It is worth remembering the point of security is not to "be secure" at all costs, it's to guard against identified threat models while still meeting system requirements. The most secure application is of course an application that doesn't offer any service, but it's also completely pointless.
4

One use case for having separate access and refresh tokens is where your application has both client side (frontend) and server side (backend) components.

Users log in to your application, with authentication handled in your own backend - and use OAuth to access a specific resource they own.

You only expose the access token to the frontend (which is, in this scenario, more vulnerable to attackers) and keep the refresh token on the backend.

It goes something like this:

  • User logs in to your application, then wants to interact with external resource
  • Your frontend redirects to the resource's Authentication server
  • User enters credentials
  • Authentication server redirects user to your frontend with an OAuth code
  • Your frontend contacts your backend with the OAuth code
  • Your backend contacts the Authentication server with the OAuth code and an OAuth secret (that is only stored in your backend)
  • Authentication server sends back access token AND refresh token to your backend
  • Backend keeps hold of refresh token, and sends access token to the frontend.

Now your frontend has access to the access token - but not the refresh token. When the access token expires, your frontend can then contact your backend and ask for a new one. Your backend can then use the refresh token to get a new access token, and send it to the frontend.

Because users need to be logged in to your backend, you are already providing a layer of security, and it's ok to have longer lived refresh tokens.

Now you just gotta hope no one's going to break into your backend ;)

4

If we consider that having a valid refresh token allows you to get a valid access token, then the two can be considered to have the same 'informational value' right?

I would say that depends on backend implementation. The access token is typically used to grant unlimited access to authorized resources, but in a very short time, usually by hours. Meanwhile, though refresh tokens may have very long lifetime, they have more restrictions than access token. That is, a refresh tokens allow you to get only access tokens, and it is able to be used only once for a period of time (once per 60 minutes per user for example). Not to say that some systems track refresh tokens in their databases, allow them to verify and invalidate any refresh tokens, as well as access tokens (although this point may violate RESTful principals, but that is another topic). Anyway, as you can see, due to its restrictions, the probability which refresh token is stolen is much less than access token

I don't understand the different treatment they receive from a security standpoint unless we look at this topic within a context of massive security flaws.

The reason why they have different treatment is that they serve different purposes. While access token is mean to provide an authentication mechanism, refresh token is mean to provide the "remember me" functionality. In other words, you don't really need refresh token in order to make your authentication system work. However, end users may find your system hard and frustrated to use, no matter how secure your systems are. They have to re-login, and even do 2FA authentication everytime an access token expired (maybe once per a hour). Thus, refresh token is used to provide a mechanism to balance security with usability, as well as convenience

If we for example argue that there could be a critical security leak within the https protocol, then sure, an attacker could probably steal an access token more easily since it's exchanged more often between a client and a server.

To be fair, any arbitrary security mechanisms both have pros and cons, advantages and weaknesses. Oauth 2.0 authorization framework itself is not a religion, and it is not completely immune to cyber attacks as well. That's why beside basic token-based authentication, companies such as Google provide additional authentication methods like 2FA. Depending on your use case, you can implement various method to protect users'data and to enhance users' experiences without using access tokens and refresh token (like SAML for example) as well

Some references you may want to read:

Why Does OAuth v2 Have Both Access and Refresh Tokens?

Where to store JWT refresh tokens?

When to use Refresh Token?

RFC 6749: The OAuth 2.0 Authorization Framework

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .