Access and Refresh Tokens

Access tokens are short-lived credentials presented to APIs, while refresh tokens are longer-lived credentials used to obtain new access tokens. This separation lets an application maintain a longer user session without making the credential sent to APIs valid for as long.

An access token represents authorization to call protected resources. A client typically sends it with each API request, commonly in the `Authorization: Bearer <token>` header. Because it is used frequently and may be exposed to more components, its lifetime is usually kept relatively short.

A refresh token serves a different purpose: it is presented to the authorization server to obtain a new access token after the current one expires. It normally is not sent to ordinary resource APIs. The authorization server can use the refresh-token request to enforce session policies, revoke sessions, or require additional checks.

A typical flow is: the user authenticates, the authorization server issues an access token and refresh token, the client uses the access token for API calls, and the client later exchanges the refresh token for a new access token. With refresh-token rotation, the server issues a new refresh token during renewal and invalidates the previous one, reducing the usefulness of a stolen token.

In a Java backend, token validation commonly checks the access token's signature or introspects it, then verifies claims such as expiration, issuer, audience, and scopes or authorities. The backend should distinguish authentication from authorization: a valid token establishes an authenticated principal, while scopes or roles determine what that principal may access.

Refresh tokens require stronger protection than ordinary short-lived access tokens because their longer lifetime makes theft more consequential. Their storage and transport depend on the client type; for browser applications, designs often use secure, appropriately scoped cookies and server-side session controls rather than exposing long-lived credentials to JavaScript.

Key points

Interview questions

Why use both access tokens and refresh tokens instead of one long-lived token?

The access token is the credential used frequently against protected APIs, so keeping it short-lived limits the useful lifetime of a stolen token. The refresh token supports a longer user session without requiring the access token itself to remain valid for a long time. The refresh token is normally handled only by the authorization server. This separates API authorization from session renewal.

What happens when an access token expires?

The resource server should reject the expired access token rather than accepting it. The client can then send its refresh token to the authorization server to request a new access token, assuming the refresh token and session remain valid. With rotation, the authorization server may also return a replacement refresh token and invalidate the old one. The client then retries the API operation using the new access token when appropriate.

What should a resource server validate in a JWT access token?

It should verify the token's cryptographic signature using the appropriate trusted key or validation mechanism. It should also check claims relevant to the API, including expiration, issuer, audience, and permissions such as scopes or authorities. Signature validity alone is not enough because a correctly signed token can still be intended for another service or be expired. The exact checks depend on the authorization architecture and token contract.

How does refresh-token rotation improve security?

With rotation, each successful refresh produces a new refresh token and makes the previous token unusable. If an attacker later presents an already-used refresh token, the authorization server can detect possible replay and take action such as revoking the associated token family or session. The exact response is an architectural choice, but the key benefit is that theft of a refresh token does not automatically provide an indefinitely reusable credential. Rotation therefore combines credential replacement with replay detection.

Where would you store refresh tokens in a browser-based application?

The answer depends on the browser architecture and threat model, but a common security-oriented design keeps a refresh credential in a cookie that is inaccessible to JavaScript, with appropriate `Secure` and `HttpOnly` attributes and a suitable `SameSite` policy. CSRF protections must also be considered because cookies are automatically attached to requests under applicable conditions. Simply choosing browser storage without analyzing XSS and CSRF risks is insufficient. For highly sensitive applications, a backend-for-frontend or server-side session can further reduce exposure of long-lived credentials to browser code.