Skip to content

Refresh Tokens — Long-Lived Credentials for Continuous API Access

DodaTech Updated 2026-06-28 5 min read

In this tutorial, you will learn about Refresh Tokens. We cover key concepts, practical examples, and best practices to help you master this topic.

Refresh tokens are long-lived credentials issued alongside access tokens, allowing clients to obtain new access tokens without requiring the user to re-authenticate.

What You'll Learn

How refresh tokens work, refresh token rotation, secure storage, and implementation for seamless API access across sessions.

Why It Matters

Short-lived access tokens (15-60 minutes) limit damage if stolen. But asking users to log in every hour is impractical. Refresh tokens provide the best of both worlds: frequent token rotation with infrequent user re-authentication.

Real-World Use

Google APIs issue refresh tokens that last until revoked. GitHub tokens can be configured with expiry. DodaTech's Durga Antivirus Pro uses refresh tokens so partner integrations maintain continuous access without user interaction.

flowchart LR
    A["Client"] -->|"Login"| B["Auth Server"]
    B -->|"Access Token (15 min)\n+ Refresh Token (30 days)"| A
    A -->|"API call + Access Token"| C["Resource Server"]
    C -->|"Expired"| D["401 Unauthorized"]
    D -->|"POST /refresh + Refresh Token"| B
    B -->|"New Access Token + New Refresh Token"| A
    A -->|"New API call"| C
    C -->|"200 OK"| A
    style A fill:#dbeafe,stroke:#2563eb
    style B fill:#fef3c7,stroke:#d97706
    style C fill:#dcfce7,stroke:#16a34a
    style D fill:#fecaca,stroke:#dc2626

Access Token vs Refresh Token

Property Access Token Refresh Token
Lifetime Short (15-60 min) Long (days to months)
Sent with API calls Yes No
Can be revoked Hard (stateless JWT) Yes (server-side)
Contains data User claims, scopes Reference or opaque
Rotation Implicit (new one issued) Optional (rotate on use)

Code Example: Refresh Token Flow

import requests
import time

TOKEN_URL = "https://auth.example.com/oauth/token"
API_URL = "https://api.example.com/data"
CLIENT_ID = "my-app"
CLIENT_SECRET = "my-secret"

# Simulated stored tokens
tokens = {
    "access_token": None,
    "refresh_token": None,
    "expires_at": 0
}

def get_valid_token():
    """Return a valid access token, refreshing if needed."""
    if time.time() < tokens["expires_at"]:
        return tokens["access_token"]

    if not tokens.get("refresh_token"):
        raise Exception("No refresh token available. Re-authenticate.")

    # Refresh the access token
    response = requests.post(TOKEN_URL, data={
        "grant_type": "refresh_token",
        "refresh_token": tokens["refresh_token"],
        "client_id": CLIENT_ID,
        "client_secret": CLIENT_SECRET
    })

    if response.status_code != 200:
        raise Exception("Refresh failed. Re-authenticate.")

    new_tokens = response.json()
    tokens["access_token"] = new_tokens["access_token"]
    tokens["expires_at"] = time.time() + new_tokens["expires_in"]

    # Refresh token rotation
    if "refresh_token" in new_tokens:
        tokens["refresh_token"] = new_tokens["refresh_token"]

    return tokens["access_token"]

# First call — exchange code for tokens
code_response = requests.post(TOKEN_URL, data={
    "grant_type": "authorization_code",
    "code": "auth-code",
    "client_id": CLIENT_ID,
    "client_secret": CLIENT_SECRET
})

initial = code_response.json()
tokens["access_token"] = initial["access_token"]
tokens["refresh_token"] = initial["refresh_token"]
tokens["expires_at"] = time.time() + initial["expires_in"]

# Later API call — auto-refreshes if token expired
access_token = get_valid_token()
response = requests.get(
    API_URL,
    headers={"Authorization": f"Bearer {access_token}"}
)
print(f"API call status: {response.status_code}")

Refresh Token Rotation

Rotation means each time a refresh token is used, the server returns a new refresh token and invalidates the old one. This prevents a stolen refresh token from being used multiple times.

Without Rotation With Rotation
Stolen refresh token works forever Stolen refresh token works once
No way to detect token theft Concurrent use of old and new token alerts the server
Revocation requires manual intervention Automatic single-use limits damage

Common Mistakes

1. Storing Refresh Tokens in localStorage

XSS can steal refresh tokens. Store them in httpOnly cookies (web) or secure device storage (mobile).

2. Not Using Refresh Token Rotation

Without rotation, a stolen refresh token is valid until it expires. Rotation limits the window of opportunity.

3. Making Refresh Tokens Immortal

Refresh tokens should expire (30-90 days). For sensitive apps, shorter expiry combined with rotation is best.

4. Not Handling Refresh Token Rejection

A 401 on refresh may mean the token is revoked, expired, or already used. Redirect the user to log in again.

5. Sending Refresh Tokens with Every API Call

Refresh tokens are only for the token endpoint. Never send them with regular API calls.

Practice Questions

  1. What is the purpose of a refresh token?
  2. Why are access tokens short-lived while refresh tokens are long-lived?
  3. What is refresh token rotation?
  4. How should clients detect that a refresh token is invalid?
  5. What happens when a refresh token expires?

Answers:

  1. A refresh token obtains new access tokens without requiring the user to re-authenticate.
  2. Access tokens are sent with every API call — short expiry limits damage if stolen. Refresh tokens are sent rarely and can be revoked server-side.
  3. Rotation means each refresh request returns a new refresh token and invalidates the old one. A stolen token can only be used once.
  4. The refresh endpoint returns 401 or invalid_grant. The client should redirect the user to re-authenticate.
  5. The client receives an error on the refresh attempt. The user must re-authenticate to obtain a new refresh token.

Challenge: Implement a complete refresh token system with rotation, server-side tracking of token lineage, and automatic detection of token theft (when an old, already-used token is presented).

FAQ

How long should refresh tokens last?

7-30 days for most applications. 90 days for internal tools with lower security requirements. Never make them permanent.

Can refresh tokens be revoked?

Yes. The authorization server maintains a list of valid refresh tokens. Deleting or marking one as invalid revokes it.

Should refresh tokens be JWTs?

Not typically. Refresh tokens are often opaque strings that reference server-side state. Opaque tokens enable immediate revocation.

What happens if a refresh token is stolen?

With rotation, the thief can use it once. If the legitimate user refreshes before the thief, the thief's next attempt will fail (and alert the server).

Can I have multiple valid refresh tokens?

Yes. A user can have multiple devices, each with its own refresh token. The server maintains a set of valid tokens per user.

Mini Project

Build a token management service with refresh token rotation: an endpoint that issues access + refresh tokens, a refresh endpoint that rotates both, automatic detection of replay attacks, and revocation support.

What's Next

Now compare API Keys vs JWT to understand when each authentication method is appropriate.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro