Stop 10 · API IntegrationTopics · 10/16

Refresh Token

Part of the Frontend Developer Roadmap

In short

Short-lived access tokens are safer, but nobody wants to log in every 15 minutes. Refresh tokens quietly fetch a new one in the background, so users never notice.

A longer-lived token used to silently obtain a new access token once the short-lived one expires, avoiding forcing the user to log in again.

The jaadu shows an ID card at office reception and gets a fresh visitor pass.

Think of it like this

Day pass and office ID card

At a big office, the guard gives you a visitor pass that expires by evening. Your long-term ID card stays in your wallet. When the pass expires, you show the ID at the desk and get a fresh pass, without the full check again.

Watch it happen

A silent refresh

App
API
Auth server

GET /me (old token)

1/6The access token expired a minute ago, but the app doesn't know yet.

The big ideas

What you need to know

Two tokens, two lifetimes

The access token lives minutes and goes with every request. The refresh token lives days and is used only to get a new access token.

Keep the refresh token safe

Ideally it sits in an httpOnly cookie, which JavaScript can't read, so a script injected by an attacker can't steal it.

Refresh on 401

When a request fails with 401, call /auth/refresh, get a new access token, then retry the original request.

Only refresh once

If five requests fail together, share one refresh call instead of firing five.

See it in code

Walk through the code

ts
1let refreshing: Promise<string> | null = null;2 3api.interceptors.response.use(undefined, async (error) => {4  const original = error.config;5  if (error.response?.status !== 401 || original._retried) throw error;6  original._retried = true;7 8  refreshing ??= api.post("/auth/refresh").then((r) => r.data.accessToken);9  const token = await refreshing.finally(() => (refreshing = null));10 11  original.headers.Authorization = `Bearer ${token}`;12  return api(original);13});

1/4One shared promise, so many failed requests wait on a single refresh.

One shared refreshing promise means many failed calls wait on a single refresh. The _retried flag stops an endless loop if the new token also fails.

Save yourself the pain

Common mistakes

Oops

Retrying forever when the refresh endpoint itself returns 401.

Do this instead

Mark requests as retried once. If refresh fails, clear the session and send the user to login.

Oops

Storing the refresh token in localStorage next to the access token.

Do this instead

Prefer an httpOnly, Secure cookie set by the server, so page scripts can't read it.

Your mission

Simulate an expired token

~40 min

Quick check

Why is the access token kept short-lived when a refresh token exists?

Go deeper

  1. 1Diagram the full flow: access token expires → refresh token exchanged → new access token issued.
  2. 2Learn why access tokens are kept short-lived and refresh tokens longer-lived.
  3. 3Trace through automatic token refresh via an Axios response interceptor.

More topics at this stop

Stuck on this topic? Ask an Insider

Get 1:1 guidance from people who've walked this exact path — on the InsideEdge app.

Download
InsideEdge