Access Tokens vs Refresh Tokens: The Missing Piece Most JWT Tutorials Don't Explain

When I first learned JWT authentication in MERN, I understood how to generate a token after login. What I didn't understand was why we need both an Access Token and a Refresh Token.
If a user is already logged in, why does the server keep asking for tokens? Why not just trust that the user logged in earlier?
After implementing authentication middleware and building login/logout flows, the answer finally clicked.
In this article, I'll explain Access Tokens and Refresh Tokens from a practical developer's perspective
most of the information i got is by reading some articles , tutorials and llms
The Fundamental Problem
HTTP is stateless.
Every request sent to the server is independent.
Imagine a user logs in:
POST /login
The server verifies the email and password and generates a JWT.
The user then makes another request:
GET /profile
The server doesn't automatically remember who sent this request.
It only sees:
Incoming Request
The server needs proof that the request is coming from an authenticated user.
That's where tokens come in.
Access Token
An Access Token is a short-lived JWT used to access protected routes.
Example payload:
{
"_id": "user123"
}
The token is sent with every protected request:
Authorization: Bearer ACCESS_TOKEN
When the request reaches the server, middleware verifies:
Does a token exist?
Was it signed by our server?
Has it expired?
Does the user still exist?
Example middleware:
const decodedToken = jwt.verify(
token,
process.env.ACCESS_TOKEN_SECRET
)
const user = await User.findById(decodedToken._id)
If everything is valid:
req.user = user
next()
The request proceeds.
Why Not Use Access Tokens Forever?
Imagine an access token that lasts for 30 days.
If an attacker steals it:
Attacker = Full Access For 30 Days
That's a security nightmare.
Instead, access tokens are intentionally short-lived:
5 minutes
15 minutes
30 minutes
1 hour
Even if stolen, the damage is limited.
Refresh Token
A Refresh Token exists for one purpose:
Generate new Access Tokens.
Instead of forcing users to log in every 15 minutes, we issue:
Access Token → 15 minutes
Refresh Token → 7-30 days
When the Access Token expires:
401 Unauthorized
The frontend sends the Refresh Token to a refresh endpoint:
POST /refresh-token
The server verifies the Refresh Token and issues a new Access Token.
The user stays logged in without noticing anything.
The Real Difference
| Access Token | Refresh Token |
|---|---|
| Used on every API request | Used only to get new access tokens |
| Short lifespan | Long lifespan |
| Higher exposure risk | More protected |
| Expires quickly | Keeps user logged in |
| Sent frequently | Sent rarely |
Why Do We Still Query the Database?
One question confused me while building authentication middleware.
If the JWT already contains the user's ID, why do we still query MongoDB?
const user = await User.findById(decodedToken._id)
The reason is simple.
A token only proves:
This user was valid when the token was created.
It does not prove:
The account still exists
The user wasn't banned
Permissions haven't changed
The database remains the source of truth.
What Happens During Logout?
Many beginners think logout means:
res.clearCookie("accessToken")
But that's only part of the story.
The real logout mechanism is usually:
user.refreshToken = undefined
await user.save()
Why?
Because if someone steals a Refresh Token, we need a way to invalidate it.
Deleting it from the database prevents future token refreshes.
Advanced Concept: Refresh Token Rotation
Most tutorials stop here.
Production systems often use Refresh Token Rotation.
Instead of reusing the same Refresh Token forever:
Refresh Token A
↓
Generate Access Token
↓
Generate Refresh Token B
↓
Invalidate Refresh Token A
This reduces the impact of stolen Refresh Tokens.
Many large-scale applications use this approach for additional security.
Final Mental Model
Think of authentication like entering a secure building.
Access Token = Temporary Entry Pass
Refresh Token = Master Pass To Get New Entry Passes
The Access Token gets you through security checkpoints.
The Refresh Token allows you to obtain a new Access Token without proving your identity again.
Together, they provide both:
Security (short-lived access)
Good user experience (persistent login)
And that's why modern applications use both instead of relying on a single token.
