Auth0’s refresh token rotation is a security feature that automatically invalidates an old refresh token and issues a new one every time a refresh token is used.

Imagine you’re using a physical key to a secure room. If that key is lost or stolen, the room remains compromised indefinitely. Refresh token rotation is like having a security guard who, every time you use your key, checks your ID, gives you a new key, and deactivates the old one. This way, if an attacker ever got hold of your old key, it would be useless by the time they tried to use it.

Let’s see it in action. We’ll simulate a client application that has an initial refresh token and then uses it to get a new access token, triggering the rotation.

First, you need to enable this feature in your Auth0 tenant.

Navigate to your Auth0 Dashboard. Go to Applications -> APIs. Select the API for which you want to enable refresh token rotation. Go to the Settings tab. Scroll down to the Refresh Token Rotation section. Toggle the switch to Enabled.

Once enabled, any new refresh tokens issued for this API will be subject to rotation. Existing refresh tokens will also be rotated on their next use.

Now, let’s look at the client-side flow. A typical OAuth 2.0 authorization code grant with PKCE would look something like this:

  1. Authorization Request: The user is redirected to Auth0’s /authorize endpoint to log in.

  2. Token Request: After successful authentication, Auth0 redirects back to your client with an authorization code. Your client exchanges this code for an access token and a refresh token using the /oauth/token endpoint.

    curl -X POST \
      https://YOUR_AUTH0_DOMAIN/oauth/token \
      -H 'content-type: application/json' \
      -d '{
        "grant_type": "authorization_code",
        "client_id": "YOUR_CLIENT_ID",
        "client_secret": "YOUR_CLIENT_SECRET", # If using a confidential client
        "code": "AUTHORIZATION_CODE_FROM_REDIRECT",
        "redirect_uri": "YOUR_REDIRECT_URI",
        "code_verifier": "PKCE_CODE_VERIFIER" # If using PKCE
      }'
    

    The response will contain an access_token, expires_in, id_token, and importantly, refresh_token.

  3. Access Token Expiration: The access_token has a short lifespan (e.g., 1 hour). When it expires, your client needs to get a new one without requiring the user to log in again. This is where the refresh token comes in.

  4. Refresh Token Request (Rotation Trigger): Your client makes a request to the /oauth/token endpoint using the refresh_token grant type.

    curl -X POST \
      https://YOUR_AUTH0_DOMAIN/oauth/token \
      -H 'content-type: application/json' \
      -d '{
        "grant_type": "refresh_token",
        "client_id": "YOUR_CLIENT_ID",
        "client_secret": "YOUR_CLIENT_SECRET", # If using a confidential client
        "refresh_token": "THE_REFRESH_TOKEN_RECEIVED_EARLIER"
      }'
    

    Crucially, with refresh token rotation enabled:

    • Auth0 validates the provided refresh_token.
    • If valid, Auth0 issues a new access_token.
    • Auth0 then invalidates the refresh_token that was just used.
    • Auth0 also issues a brand new refresh_token in the response.

    The response will look like this:

    {
      "access_token": "NEW_ACCESS_TOKEN",
      "expires_in": 3600,
      "id_token": "NEW_ID_TOKEN",
      "refresh_token": "A_COMPLETELY_NEW_REFRESH_TOKEN",
      "token_type": "Bearer"
    }
    

    Your client application must store this new refresh_token for future use and discard the old one. If an attacker previously obtained the old refresh token, it will now be invalid, and the attacker will only have access to the newly rotated, and currently unknown to them, refresh token.

The key levers you control are primarily within the Auth0 tenant settings for the API. There aren’t many direct configuration options for the rotation itself once enabled, as it’s designed to be a transparent security enhancement. The main "control" is deciding which APIs benefit from this enhanced security. You also control how your client application handles the refresh token lifecycle: it must be programmed to store the new refresh token received in the rotation response and discard the old one. Failure to do so means you’re not benefiting from the rotation and your client will eventually fail when it tries to use an invalidated token.

What most developers miss is that the refresh token itself has a lifecycle, and rotation doesn’t mean infinite refresh tokens. Auth0 enforces a maximum number of refresh tokens per user per client (defaulting to 50). If this limit is reached, older refresh tokens are automatically revoked, even if they haven’t been used. This is another layer of defense against token proliferation.

The next concept you’ll want to understand is how to manage refresh token revocation explicitly, both from the client and the administrator side.

Want structured learning?

Take the full Auth0 course →