Home / Blog / Encoding & Base64

Hash crypto & JWT

JWT Access and Refresh Tokens: Follow the Two-Credential Flow

Trace an illustrative access-token and refresh-token exchange, keep each credential at its intended endpoint, and handle replacement tokens carefully.

Give the two credentials different jobs

An access token is presented to the protected API under the authorization server's contract. A refresh token is used with the authorization server to request renewed access. The access token may be a JWT, while the refresh token may be opaque. Neither credential's purpose is determined solely by whether a decoder can read it.

Suppose a fictional notes application completes its authorized sign-in flow and receives the following token response. The strings are placeholders, not functioning credentials. The lifetime describes the access token in this example; it does not state that the refresh token has the same lifetime.

{
  "access_token": "EXAMPLE_ACCESS_TOKEN",
  "token_type": "Bearer",
  "expires_in": 300,
  "refresh_token": "EXAMPLE_REFRESH_TOKEN"
}

Use the access token for the resource request

The client sends the access token using the API's documented authorization method, commonly a Bearer Authorization header. It does not send the refresh token in that header when the first token stops working. An API that receives the wrong credential may reject it even if the credential is genuine and still active.

Keep the requested resource and token audience aligned. A token for a profile API is not automatically accepted by a billing API. Test the wrong-audience case explicitly, and do not respond to it by disabling audience validation at the resource server.

Exchange the refresh token at the documented token endpoint

POST /oauth/token HTTP/1.1
Host: auth.example.com
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token&refresh_token=EXAMPLE_REFRESH_TOKEN

This request illustrates the refresh grant's body format. The path, host, client authentication and additional parameters depend on the provider. It is not a complete request for every OAuth deployment. A confidential server client and a public browser client have different capabilities; embedding a confidential client secret in browser JavaScript does not make that browser confidential.

The OAuth refresh-token section defines this exchange. Follow the authorization server's documentation for authentication and allowed scope. A refresh request should not be used to invent broader privileges than the authorization it represents.

Treat a replacement refresh token as a state update

A successful renewal can return a new access token and a replacement refresh token. Persist the replacement according to the client's storage design before another operation tries to renew with the old value. If the provider does not return a replacement, follow its documented retention behavior rather than erasing the current credential by accident.

Use an atomic update where the storage system supports it. Saving the access token while losing its paired replacement refresh token can leave the current request working but the next renewal broken. Simulate an interrupted storage update during testing instead of assuming two separate writes always succeed together.

The OAuth security best-current-practices document describes protections against refresh-token replay. Rotation and sender-constrained designs impose requirements on both the authorization server and client. Simply changing a string in local storage does not implement those protections.

Choose storage and logging for credential sensitivity

Choose a storage design appropriate to the application architecture, including its exposure to script injection, cross-site requests and device compromise. A refresh token can remain useful longer than an individual access token, so treat it as a sensitive credential rather than harmless session metadata.

Do not include either complete token in routine logs, analytics events or URLs. Use controlled identifiers and redacted diagnostics when troubleshooting. A local JWT Decoder can inspect a synthetic access-token payload, but it does not renew credentials or verify that the real API will accept them.

Test the sequence rather than only the sign-in response

Exercise initial authorization, an authorized API call, successful renewal, replacement-token persistence and a later renewal using the replacement. Add an invalid or revoked refresh credential and verify that the client returns to the appropriate sign-in state instead of looping.

Also simulate two API calls encountering an expired token at the same time. Coordinate them around one renewal operation under the client's design. That test exposes accidental reuse of a rotated credential and ensures that a failed renewal does not leave the interface claiming the user is still fully authenticated.