Refresh Tokens

Why there are no Refresh Tokens in Ory Kratos

Vincent Kraus - May 04, 2022

We often get questions on why Refresh Tokens are not used in Ory Kratos or requests to add this mechanism to Ory Kratos. This post aims to explain what Refresh Tokens are useful for and why they are not needed in Ory Kratos. Lets talk about the difference between flows using access and Refresh Tokens and flows using Session Cookies (and Session Tokens). Ory Kratos is using the latter, so all self-service flows are based on the concept of sessions and there are no Refresh Tokens involved.

Why Refresh Tokens

To explain why we do not need Refresh Tokens in Ory Kratos it is best to look at two different models and explain the difference between access and refresh Tokens, session cookies, and session tokens.

Access Tokens are a concept from OAuth 2.0. Access Tokens are can be pass-by-value (for example in format of a Json Web Tokens (JWT)). In our case pass-by-value means the application gets an Access Token from the OAuth 2.0 server and can validate it itself to give the end-user an active session without calling another API. This makes validation of these tokens fast, but token invalidation difficult. To invalidate these tokens, we would need a list of token IDs that are no longer valid. Then, on every validation, we would need to check this list of invalidated access tokens to understand if they are still valid. That however defeats the purpose of pass-by-value completely, as we are now making API requests to check for the validity of the tokens. To solve this problem Refresh Tokens have been introduced. The reason we have the concept of Refresh Tokens in OAuth 2.0 is to provide a mechanism to invalidate tokens / token chains. To refresh an access token, we use a refresh token. The refresh token is always pass-by-reference and requires an API call. When the Refresh Token is no longer valid, because it was invalidated in the backend, the access token is not refreshable once it is expired. Usually Access Tokens are short-lived (e.g. 1 hour). When the Refresh Token is invalidated, the access token can no longer be refreshed and therefore access has been revoked. However, it can take up to a full hour (or whatever expiry time is set for the access token) until the invalidation succeeds.

Sessions in Ory Kratos

Session Cookies are something most readers are familiar with. Session Cookies represent a User Session and are usually valid as long as the user's session is valid. User Sessions can also be stored as pass-by-value (e.g. in the form of a JWT). In Ory Kratos, these Session Tokens and Session Cookies are pass-by-reference. Bear with me, I will explain in a moment why that is important here. By definition pass-by-reference means we do a lookup of the reference every time we use it. In our case we do a lookup of the token every time we validate it. This is great, because we can easily invalidate the User Session. Once we invalidate the Session Token in the backend, access would be denied immediately on the next request - unless you have a cache somewhere. This means Instant Logout is built into Ory Kratos by design, just invalidate the session in Ory Kratos and the user is logged out instantly. So all that Refresh Tokens are doing, we already can do in Ory Kratos - meaning invalidating access credentials.

The concept of Refresh Tokens also introduces additional complexity and the OAuth 2.0 specification also leaves a lot of room for interpretation:

  1. How long should Refresh Tokens be valid for?
  2. What happens if Refresh Tokens are re-used? There are two options:
    1. We invalidate all Access Tokens and Refresh Tokens. This will eventually lead token reuse issues where you accidentally refresh the token twice, leading to access being revoked even if it shouldn't have been revoked.
    2. Do not invalidate anything. This is equal to just having a long living Access Token.

So, what does the user gain from this? Not much, really:

  1. If the Refresh Token or Access Token are compromised the attacker can use them for as long as they are valid.
  2. If the Refresh Token is re-used, and the token chain is invalidated, the user would probably never know that their account was compromised because it might have been a network flake or code bug.

All of these reasons speak against implementing a refresh functionality for sessions, because the added security is negligible. Instead, we need to rely on other security mechanisms:

  • Strict TLS enforcement with certificate pinning for HTTPS connections
  • Storing credentials in the OS credentials chain

There are possibilities to detect credential theft, that will be implemented in a future version of Ory Cloud:

  • Is the credential being used from another IP, geolocation, or user agent? If so, is it reasonable or a risk?
  • Is the credential suddenly behaving different? If so, is it reasonable or a risk?

Based on these questions one can guess if a credential was stolen and misused, or has regular use. This will actually detect, and potentially prevent, credential theft. Applying Refresh Tokens to this problem doesn't bring any of these benefits. The only benefit would be that the attacker does not have long-term-access to the credential, but the account is already compromised anyways.

Refresh Tokens are something that solve a very particular problem (invalidating pass-by-value credentials), and are not generalizable over other measures such as credential theft. Refresh Tokens will not make an application more secure by design.