When building a modern web application, authentication is one of the first things we need to think about.
A simple login system sounds easy:
User enters email and password → server checks them → user is logged in.
But what happens after the login?
How does the website remember that the user is authenticated? How long should the login remain valid? What happens if an authentication token gets stolen?
This is where JWT access tokens and refresh tokens become useful.
What is a JWT?
JWT stands for JSON Web Token.
It is a compact token that can be used to represent authenticated information between a client and a server.
After a successful login, the server can generate an access token:
User → Login
↓
Server verifies credentials
↓
Server generates JWT
↓
Client receives token
The client can then send the access token with requests to protected APIs.
For example:
Authorization: Bearer
The server verifies the token and decides whether the request is authenticated.
So what's the problem with a normal JWT?
The main problem is token lifetime.
Imagine giving a user an access token that remains valid for 30 days.
That's convenient, but it creates a security problem.
If someone steals that token, they may be able to use it until it expires.
On the other hand, if we make the access token expire after 5 or 10 minutes, security improves, but users would constantly have to log in again.
That's not a great user experience.
This is where the refresh token comes in.
What is a Refresh Token?
A refresh token is a longer-lived credential that can be used to obtain a new access token without asking the user to log in again.
Instead of having one token do everything, we separate the responsibilities:
Access Token
Short-lived
Used for API requests
Expires quickly
Refresh Token
Longer-lived
Used to obtain new access tokens
Should be protected carefully
Usually isn't sent with every API request
The basic idea looks like this:
LOGIN
│
▼
┌─────────────────┐
│ Server verifies │
│ credentials │
└────────┬────────┘
│
┌──────┴──────┐
▼ ▼
Access Token Refresh Token
Short-lived Long-lived
│ │
▼ │
API Requests │
│
Access token
expires
│
▼
Refresh request
│
▼
New access token
The user doesn't need to enter their password again.
Why Not Just Use a Long-Lived JWT?
This is one of the biggest reasons refresh tokens exist.
Suppose your access token is valid for 30 days.
If it gets stolen, the attacker may have a usable credential for a long period.
Instead, you could make the access token short-lived:
Access Token → 10–15 minutes
Refresh Token → days/weeks
Now, even if an access token is compromised, its useful lifetime is much shorter.
The refresh token is more sensitive, so it needs stronger protection and should be handled carefully.
How the Login Flow Works
Let's imagine a user logs into an application.
Step 1 — User submits credentials
POST /api/login
{
"email": "user@example.com",
"password": "password"
}
The server verifies the credentials.
Step 2 — Server generates tokens
If authentication succeeds:
Access Token
+
Refresh Token
The access token is used for normal API requests.
The refresh token is kept in a safer storage mechanism, commonly an HttpOnly, Secure cookie for browser applications.
Step 3 — User accesses protected resources
The client sends the access token:
GET /api/profile
Authorization: Bearer
The server verifies it.
If valid:
200 OK
Step 4 — Access token expires
Eventually:
Access Token → EXPIRED
Instead of showing the login page immediately, the application can use the refresh token.
Step 5 — Refresh the session
The client sends a request such as:
POST /api/refresh
The server validates the refresh token.
If everything is valid, it issues a new access token.
Old Access Token → Expired
Refresh Token
↓
Server validates
↓
New Access Token
The user can continue using the website without logging in again.
Why This Matters for Website Owners
For website owners and developers, refresh tokens aren't just about making authentication more complicated.
They can provide a better balance between security and user experience.
- Shorter exposure for access tokens
Access tokens can have short lifetimes.
This reduces the period during which a stolen access token remains usable.
- Better user experience
Users don't have to repeatedly enter their passwords.
They can remain signed in while the application silently obtains new access tokens.
- Better session control
A properly designed refresh-token system can allow the server to revoke sessions.
For example:
User clicks "Log out from all devices"
↓
Server invalidates refresh tokens
↓
Existing sessions can no longer
refresh themselves
This is much more useful than simply waiting for a long-lived token to expire.
- Device/session management
A website can maintain separate sessions for different devices.
For example:
Chrome - Malaysia
iPhone - Malaysia
Laptop - Bangladesh
Each session can have its own refresh token.
If one device is lost, its session can be revoked without necessarily logging the user out everywhere.
Access Token vs Refresh Token
Feature Access Token Refresh Token
Main purpose Access APIs Obtain new access tokens
Lifetime Short Longer
Sent with API requests Usually yes Usually no
Security sensitivity High Very high
Can be revoked server-side Depends on design Commonly
Should be exposed to JavaScript Minimize exposure Prefer HttpOnly cookie
The exact implementation depends on the application's architecture and threat model.
Important Security Practices
JWT itself doesn't automatically make an authentication system secure.
The implementation matters.
Use HTTPS
Never send authentication credentials or tokens over an unencrypted HTTP connection.
Use HTTPS/TLS in production.
Keep access tokens short-lived
Don't make an access token valid for an unnecessarily long period.
A short lifetime limits the damage from token theft.
Protect refresh tokens
For browser-based applications, an HttpOnly + Secure cookie is commonly used.
HttpOnly helps prevent JavaScript from directly reading the cookie.
Secure ensures the cookie is sent only over HTTPS.
Depending on the application, SameSite settings should also be configured appropriately.
Consider refresh-token rotation
Instead of reusing the exact same refresh token forever, the server can issue a new refresh token whenever one is successfully used.
The previous token can then be invalidated.
This is known as refresh-token rotation.
It can help detect and limit certain replay attacks.
Never store passwords inside JWTs
A JWT should never contain the user's password.
Passwords should be stored using an appropriate password hashing algorithm such as Argon2id or bcrypt.
Validate tokens properly
The server should verify important JWT properties such as:
Signature
Expiration
Issuer
Audience
Algorithm
The exact checks depend on the authentication architecture.
What Happens When a User Logs Out?
A common mistake is thinking:
"Deleting the JWT from the browser means the session is completely gone."
Not necessarily.
If a refresh token remains valid on the server, it may still be possible to create another access token.
A stronger logout design can invalidate the user's refresh session.
For example:
Logout
↓
Delete/clear client session
↓
Revoke refresh token
↓
Future refresh attempts fail
This gives the server more control over active sessions.
JWT Is Not a Complete Authentication System
This is probably the most important point.
JWT is simply a token format.
Using JWT doesn't automatically mean an application has secure authentication.
A secure authentication system also needs to consider:
Password hashing
HTTPS
Token expiration
Secure cookie configuration
CSRF protection where applicable
XSS protection
Refresh-token rotation
Session revocation
Rate limiting
Account recovery
Multi-factor authentication
Proper server-side authorization
JWT is one part of the system, not the entire system.
A Simple Mental Model
I like to think about it this way:
Access token = temporary access pass
Refresh token = credential used to obtain another temporary pass
For example:
LOGIN
↓
Access Token ──────────→ API
│
│ expires
↓
Refresh Token
│
↓
New Access Token
│
↓
API
The user experiences one continuous session, while the application can keep the actual API credential short-lived.
Final Thoughts
When building a modern application, authentication is not just about making the login button work.
It's about deciding:
How long should a session last?
What happens if a token is stolen?
How can a user stay logged in without keeping a powerful credential valid forever?
Refresh tokens provide one way to solve this problem.
Used correctly, the combination of short-lived access tokens + carefully protected refresh tokens + proper session management can give developers a practical balance between security and user experience.
But the important part is not simply adding JWT.
The real security comes from designing the entire authentication flow carefully.

Top comments (0)