What Is a Refresh Token? The Two-Token Pattern
Here's the claim most auth tutorials bury: the refresh token is the credential you should guard the hardest โ and the one they barely explain. Your access token gets all the attention because it's on every request, but it's designed to be nearly worthless within minutes. The refresh token is the quiet, long-lived key behind it. Get the relationship between the two right and you get security and a login that doesn't nag users every five minutes.
The problem: one token can't be both safe and convenient
Imagine a single token that both proves who you are on every API call and lasts long enough that you're not constantly logging back in. That token is a contradiction. Make it long-lived for convenience and a single leak โ a logged URL, a stolen laptop, a cached proxy โ hands an attacker weeks of access. Make it short-lived for safety and your users re-enter their password every ten minutes. You can't win with one token.
The fix: split the job in two
The standard answer is the two-token pattern. You issue two credentials with different lifetimes and different jobs:
- An access token โ short-lived (minutes), sent on every request to prove you're allowed in. It's usually a JSON Web Token you can decode and inspect that rides in the Authorization header as a bearer token.
- A refresh token โ long-lived (days or weeks), sent to exactly one endpoint whose only purpose is to hand back a fresh access token. It never touches your regular API.
When the access token expires, the client quietly exchanges the refresh token for a new one. The user notices nothing.
The hotel front desk analogy
Think of checking into a hotel. Your room key card is the access token: it opens your door, it's what you carry around, and it's deliberately programmed to stop working at checkout. If you drop it in the lobby, whoever finds it has a key that expires soon โ and opens one door, not the vault.
Your reservation at the front desk is the refresh token. You don't wave it at every door; you present it once, at one place, to get a freshly cut key card when yours stops working. It's more powerful, so it lives somewhere safer โ behind the desk, tied to your identity โ and the hotel can cancel it the moment you check out. That's the whole model: a disposable thing you use constantly, backed by a valuable thing you use rarely and protect carefully.
Why not just make access tokens last longer?
Because a JWT access token usually can't be revoked. As the guide on JWT vs session cookies explains, a signed token is self-contained โ the server has nothing to delete, so it stays valid until its exp timestamp passes, no matter what. A long-lived access token is therefore a long-lived liability. Keeping it short (and reading its expiry with the JWT Expiration Checker when you're debugging a 401) caps the blast radius of a leak. The refresh token restores the convenience the short lifetime took away โ see when a JWT expires for exactly how that clock works.
Refresh tokens are revocable โ and that matters
Here's the part people miss: unlike a stateless access token, the auth server does keep state for refresh tokens. It knows which ones it issued. That's what makes 'log out everywhere' and 'sign out this device' possible โ the server marks the refresh token invalid, and once the current short access token expires (minutes later), that session is genuinely dead. You get most of the revocation power of server-side sessions while keeping stateless, fast access tokens for the actual API traffic.
Handling refresh tokens safely
Because a refresh token can mint new access tokens, treat it as a top-tier secret:
- Store it out of reach of scripts. An httpOnly, Secure cookie or platform secure storage โ never
localStorage, where any XSS can read it. - Rotate on use. Issue a new refresh token each time one is redeemed and invalidate the old one; if a retired token is ever replayed, you've detected theft.
- Give it a real expiry and let users revoke it. Long-lived isn't immortal.
- Never log it or store secrets beside it. If your access token is a JWT, remember its payload is only Base64url-encoded โ decode a copy locally with the JWT Decoder, and pull tokens out of an Authorization header cleanly with the Bearer Token Parser.
The takeaway
A refresh token isn't a second password or a fancier access token โ it's a deliberately narrow, long-lived key whose only job is to fetch short-lived access tokens without dragging the user back to the login screen. Short access tokens keep leaks cheap; the refresh token keeps the experience smooth; and because the server can revoke it, you keep real control over sessions. Split the job in two, protect the powerful half, and hand out the disposable half freely.
Try the tools
Frequently Asked Questions
What is a refresh token?
A refresh token is a long-lived credential issued at login whose sole purpose is to obtain new short-lived access tokens. You don't send it on normal API requests โ only to a single renewal endpoint when your access token expires, so the user stays logged in without re-entering a password.
What's the difference between an access token and a refresh token?
An access token is short-lived and sent on every request to prove you're allowed in. A refresh token is long-lived and sent only to get a new access token. The access token is used constantly and expires fast; the refresh token is used rarely and guarded carefully.
Where should I store a refresh token?
In an httpOnly, Secure cookie or the platform's secure storage โ never in localStorage, where any cross-site scripting (XSS) can read it. Because a refresh token can mint new access tokens, treat it as a top-tier secret and keep it out of reach of page scripts.
Can a refresh token be revoked?
Yes. Unlike a self-contained JWT access token, the auth server keeps state for refresh tokens, so it can mark one invalid. Once the current short-lived access token expires minutes later, that session is genuinely dead โ which is how 'log out everywhere' and 'sign out this device' work.
How long should a refresh token last?
Long enough to avoid nagging users (often days to weeks) but not forever. Pair a real expiry with rotation โ issue a new refresh token each time one is redeemed and invalidate the old one โ so a stolen token has a limited, detectable lifespan.
Ravi Menon writes for CodeUtilityKit, where the team builds free, privacy-first developer tools that run entirely in your browser. Every guide is written and reviewed by developers who use these tools daily.