This post will explore the concept of refresh tokens as defined by OAuth 2.0. We will learn how they compare to other token types and how they let us balance security, usability, and privacy.
You can follow the text in this post, or if you prefer learning from presentations, you can watch this article’s companion video:
What Is A Token?
Tokens are pieces of data that carry just enough information to facilitate the process of determining a user's identity or authorizing a user to perform an action. All in all, tokens are artifacts that allow application systems to perform the authorization and authentication process.
New to identity concepts? Read Authentication vs Authorization to get started.
Common identity frameworks and protocols use token-based strategies to secure access to applications and resources. For example, we can use OAuth 2.0 for authorization and OIDC for authentication.
OAuth 2.0 is one of the most popular authorization frameworks out there. It is designed to allow an application to access resources hosted by other servers on behalf of a user. OAuth 2.0 uses Access Tokens and Refresh Tokens.
OpenID Connect (OIDC) is an identity protocol that performs user authentication, user consent, and token issuance. OIDC uses ID Tokens.
Want to get up to speed with OAuth2 and OpenID Connect?Download the free ebook
Let's explore the three token types that we use with OAuth 2.0 and OpenID Connect to fulfill the authentication and authorization processes of our application systems. In the process, we'll see the critical role that refresh tokens play in helping developers build applications that offer convenience without compromising security.
What's an ID token?
As the name may suggest, an ID token is an artifact that client applications can use to consume the identity of a user. For example, the ID token can contain information about the name, email, and profile picture of a user. As such, client applications can use the ID token to build a user profile to personalize the user experience.
An authentication server that conforms to the OpenID Connect (OIDC) protocol to implement the authentication process issues its clients an ID token whenever a user logs in. The consumers of ID tokens are mainly client applications such as Single-Page Applications (SPAs) and mobile applications. They are the intended audience.
What's an access token?
When a user logins in, the authorization server issues an access token, which is an artifact that client applications can use to make secure calls to an API server. When a client application needs to access protected resources on a server on behalf of a user, the access token lets the client signal to the server that it has received authorization by the user to perform certain tasks or access certain resources.
OAuth 2.0 doesn't define a format for access tokens. At Auth0, for example, access tokens issued for the Management API and access tokens issued for any custom API that you have registered with Auth0 follow the JSON Web Token (JWT) standard. Their basic structure conforms to the typical JWT structure, and they contain standard JWT claims asserted about the token itself.
Interested in getting up-to-speed with JWTs as soon as possible?Download the free ebook
This is the content of a decoded access token that follows the JWT format:
"scope": "openid profile email address phone read:appointments"
It's important to highlight that the access token is a bearer token. Those who hold the token can use it. The access token then acts as a credential artifact to access protected resources rather than an identification artifact. Malicious users could theoretically compromise a system and steal access tokens, which in turn they could use to access protected resources by presenting those tokens directly to the server.
As such, it's critical to have security strategies that minimize the risk of compromising access tokens. One mitigation method is to create access tokens that have a short lifespan: they are only valid for a short time defined in terms of hours or days.
There are different ways that a client application can get a new access token for a user. For example, once an access token expires, the client application could prompt the user to log in again to get a new access token. Alternatively, the authorization server could issue a refresh token to the client application that lets it replace an expired access token with a new one.
What Is a Refresh Token?
As mentioned, for security purposes, access tokens may be valid for a short amount of time. Once they expire, client applications can use a refresh token to "refresh" the access token. That is, a refresh token is a credential artifact that lets a client application get new access tokens without having to ask the user to log in again.
In the diagram above, SPA = Single-Page Application; AS = Authorization Server; RS = Resource Server; AT = Access Token; RT = Refresh Token.
The client application can get a new access token as long as the refresh token is valid and unexpired. Consequently, a refresh token that has a very long lifespan could theoretically give infinite power to the token bearer to get a new access token to access protected resources anytime. The bearer of the refresh token could be a legitimate user or a malicious user. As such, security companies, such as Auth0, create mechanisms to ensure that this powerful token is mainly held and used continuously by the intended parties.
When to Use Refresh Tokens
It's important to keep in mind that the OAuth 2.0 specification defines access tokens and refresh tokens. So, if we were to discuss authorization strategies in terms of other identity protocols or frameworks, such as SAML, we would not have the concepts of access tokens or refresh tokens.
For those involved with web development, access token and refresh tokens are common talk because the web extensively uses token-based authorization and authentication through the OAuth 2.0 framework and the OpenID Connect protocol.
When combined, OAuth 2.0 and OIDC bring to life an array of authorization and authentication flows. Each flow has its own set of benefits and caveats that define the best scenarios and architecture where we should use access and refresh tokens.
Is the client a traditional web application executing on the server? Use the Authorization Code Flow.
Is the client a Single-Page Application (SPA)? Use Authorization Code Flow with Proof Key for Code Exchange (PKCE).
Is the client a Single-Page Application (SPA) that doesn't need an access token? Use the Implicit Flow with Form Post.
Is the client the resource owner? You may use the Client Credentials Flow.
Is the client absolutely trusted with user credentials? You may use the Resource Owner Password Flow.
If there's an app for that, there's also a flow for that!
Keep in mind that according to the spec, when using the Implicit Flow, the authorization server should not issue refresh tokens. The Implicit flow is often implemented in Single-Page Applications (SPAs), which run on the frontend layer of a system architecture. There's no easy way of keeping a refresh token secure in the frontend layer on its own.
Using the Authorization Code Flow with Proof Key for Code Exchange (PKCE) mitigates many risks inherent to the Implicit Flow. For example, when using the implicit grant type, the access token is transmitted in the URI fragment, which can expose it to unauthorized parties. You can learn more about these vulnerabilities by reading the "Misuse of Access Token to Impersonate Resource Owner in Implicit Flow" section of the spec.
However, implementing PKCE in your applications still has no impact on how secure refresh tokens are.
However, you may not need refresh tokens.
There are scenarios where you can still get an access token without interrupting the user and without relying on the almighty power of the refresh token. Other examples to keep a session going can be cookies or silent authentication.
However, billions of people use SPAs every day. It is important to provide users with a user experience that balances security and convenience well. Is there anything that we could do to let SPAs afford the convenience of refresh tokens in a less risky and more secure manner?
An identity platform that offers Refresh Token Rotation makes it acceptable to use refresh tokens with Single-Page Applications. The spec underlines that when you can not verify that a refresh token belongs to a client, such a SPA, we should not use them unless we have Refresh Token Rotation in place.
Let's learn more about this security strategy in the next section.
Keeping Refresh Tokens Secure
A short-lived access token helps improve the security of our applications, but it comes with a cost: when it expires, the user needs to log in again to get a new one. Frequent re-authentication can diminish the perceived user experience of your application. Even if you are doing so to protect their data, users may find your service frustrating or difficult to use.
A refresh token can help you balance security with usability. Since refresh tokens are typically longer-lived, you can use them to request new access tokens after the shorter-lived access tokens expire.
However, since refresh tokens are also bearer tokens, we need to have a strategy in place that limits or curtails their usage if they ever get leaked or become compromised. All those who hold the refresh tokens have the power to get new access tokens whenever they want. "They" could be legitimate users or attackers.
At Auth0, we created a set of features that mitigate the risks associated with using refresh tokens by imposing safeguards and controls on their lifecycle. Our identity platform offers refresh token rotation, which also comes with automatic reuse detection.
Let's dive deeper into this security technique.
Refresh Token Rotation
Until very recently, a robust strategy to help SPAs maintain the user's session was using the Authorization Code Flow with PKCE in conjunction with silent authentication. Refresh token rotation is a technique for getting new access tokens using refresh tokens that goes beyond silent authentication.
Refresh token rotation guarantees that every time an application exchanges a refresh token to get a new access token, a new refresh token is also returned. Therefore, you no longer have a long-lived refresh token that could provide illegitimate access to resources if it ever becomes compromised. The threat of illegitimate access is reduced as refresh tokens are continually exchanged and invalidated.
For example, with refresh token rotation enabled in the Auth0 Dashboard, every time your application exchanges a refresh token to get a new access token, the authorization server also returns a new refresh-access token pair. This safeguard helps your app mitigate replay attacks resulting from compromised tokens.
Refresh Token Automatic Reuse Detection
Refresh tokens are bearer tokens. It's impossible for the authorization server to know who is legitimate or malicious when receiving a new access token request. We could then treat all users as potentially malicious.
How could we handle a situation where there is a race condition between a legitimate user and a malicious one? For example:
🐱 Legitimate User has 🔄 Refresh Token 1 and 🔑 Access Token 1.
😈 Malicious User manages to steal 🔄 Refresh Token 1 from 🐱 Legitimate User.
🐱 Legitimate User uses 🔄 Refresh Token 1 to get a new refresh-access token pair.
The 🚓 Auth0 Authorization Server returns 🔄 Refresh Token 2 and 🔑 Access Token 2 to 🐱 Legitimate User.
😈 Malicious User then attempts to use 🔄 Refresh Token 1 to get a new access token. Pure evil!
What do you think should happen next? Would 😈 Malicious User manage to get a new access token?
This is what happens when your identity platform has 🤖 Automatic Reuse Detection:
The 🚓 Auth0 Authorization Server has been keeping track of all the refresh tokens descending from the original refresh token. That is, it has created a "token family".
The 🚓 Auth0 Authorization Server recognizes that someone is reusing 🔄 Refresh Token 1 and immediately invalidates the refresh token family, including 🔄 Refresh Token 2.
The 🚓 Auth0 Authorization Server returns an Access Denied response to 😈 Malicious User.
🔑 Access Token 2 expires, and 🐱 Legitimate User attempts to use 🔄 Refresh Token 2 to request a new refresh-access token pair.
The 🚓 Auth0 Authorization Server returns an Access Denied response to 🐱 Legitimate User.
The 🚓 Auth0 Authorization Server requires re-authentication to get new access and refresh tokens.
It's critical for the most recently-issued refresh token to get immediately invalidated when a previously-used refresh token is sent to the authorization server. This prevents any refresh tokens in the same token family from being used to get new access tokens.
This protection mechanism works regardless of whether the legitimate or malicious user is able to exchange 🔄 Refresh Token 1 for a new refresh-access token pair before the other. Without enforcing sender-constraint, the authorization server can't know which actor is legitimate or malicious in the event of a replay attack.
Automatic reuse detection is a key component of a refresh token rotation strategy. The server has already invalidated the refresh token that has already been used. However, since the authorization server has no way of knowing if the legitimate user is holding the most current refresh token, it invalidates the whole token family just to be safe.
Refresh Tokens Help Us Embrace Privacy Tools
Privacy is a hot topic in our digital world. We not only need to balance security with convenience, but we also need to add privacy to the balancing act.
Recent developments in browser privacy technology, such as Intelligent Tracking Prevention (ITP), prevent access to the session cookie, requiring users to reauthenticate.
There is no persistent storage mechanism in a browser that can assure access by the intended application only. As such, long-lived refresh tokens are not suitable for SPAs as there are vulnerabilities that malicious users could exploit to obtain these high-value artifacts, granting them access to protected resources.
Because refresh token rotation does not rely on access to the Auth0 session cookie, it is not affected by ITP or similar mechanisms.
However, a refresh token could have its lifespan limited by the lifespan of an access token. This means we can safely use refresh tokens to play along with browser privacy tools and provide continuous access to end-users without disrupting the user experience.
You Can Store Refresh Token In Local Storage
Yes, you read that right. When we have refresh token rotation in place, we can store tokens in local storage or browser memory.
You may have heard before (maybe from us) that we should not store tokens in local storage.
However, we can reduce the absolute token expiration time of tokens to reduce the security risks of storing tokens in local storage. This reduces the impact of a reflected XSS attack (but not of a persistent one). A refresh token may have a long lifespan by configuration. However, the defined long lifespan of a refresh token is cut short with refresh token rotation. The refresh is only valid within the lifespan of the access token, which would be short-lived.
Use Refresh Tokens in Your Auth0 Apps
If you're interested in adding authentication and authorization to your application in just a few steps, sign up for a free Auth0 account now.
Our "Token Best Practices" document outlines some basic considerations to keep in mind when using tokens:
- Keep it secret. Keep it safe.
- Do not add sensitive data to the payload.
- Give tokens an expiration.
- Embrace HTTPS.
- Consider all of your authorization use cases.
- Store and reuse.
The Auth0 Dashboard makes it easy to configure your authentication and authorization services to use refresh tokens. Auth0 SDKs and libraries support refresh tokens for web applications, Single-Page Applications (SPAs), and native/mobile apps.
For additional resources on how to use refresh tokens with Auth0, please visit any of these documents: