Everything You Need to Know About Access Tokens and Refresh Tokens


Introduction
In modern web development, securing user authentication and authorization is critical. With the advent of Single Page Applications (SPAs) and mobile apps, traditional server-side sessions are being replaced by tokens for managing access control.
Two essential components in this process are Access Tokens and Refresh Tokens. These tokens are used for securely authorizing a user and refreshing sessions without the need for constant re-login.
This article will dive deep into the concepts of Access Tokens and Refresh Tokens, their roles in authentication systems, security considerations, and provide examples for implementation using Node.js.
What is an Access Token?
An Access Token is a short-lived credential that allows the user to access a protected resource. It’s typically issued after a user logs in and is sent with every subsequent request to authenticate that the user is authorized.
Key Features of Access Tokens:
Short lifespan: Typically expires in minutes to reduce the attack window.
Contains claims: Can include user information like ID, roles, and scope (permissions).
Format: Often a JWT (JSON Web Token), but can also be an opaque string.
How It Works:
When a user logs in, the server issues an Access Token, which is then included in the Authorization header of each request.
Example of an Authorization header using Bearer Token:
Authorization: Bearer <ACCESS_TOKEN>
What is a Refresh Token?
A Refresh Token is used to obtain a new Access Token when the original one expires. Unlike Access Tokens, Refresh Tokens are usually long-lived, which means they can last for days or weeks.
Key Features of Refresh Tokens:
Long lifespan: Generally remains valid for a long period (e.g., days or weeks).
Used to refresh Access Tokens: Can be sent to the server to get a new Access Token without requiring the user to reauthenticate.
Never sent with every API request: Unlike Access Tokens, Refresh Tokens are not included in each API request. They are only used to request new Access Tokens.
Security Consideration:
Since the Refresh Token is used to generate new Access Tokens, it should be stored securely (e.g., HttpOnly cookies or securely on the server).
How Access and Refresh Tokens Work Together
Here’s a step-by-step flow on how Access Tokens and Refresh Tokens work together to maintain secure user sessions:
User Logs In:
The server authenticates the user.
The server responds with an Access Token (short-lived) and a Refresh Token (long-lived).
Making API Requests:
- The client sends the Access Token in the Authorization header for each API request.
Access Token Expiration:
- When the Access Token expires, the client sends the Refresh Token to the server to request a new Access Token.
New Access Token Issued:
- The server verifies the Refresh Token and, if valid, issues a new Access Token to the client.
Repeat:
- This process continues until the Refresh Token itself expires.
Diagram: Access Token & Refresh Token Flow
+---------------------+
| User Logs In |
+---------------------+
|
v
+---------------------+ +---------------------+
| Access Token + | --> | API Request |
| Refresh Token | | (Authorization: |
| Issued to User | | Bearer <access> |
+---------------------+ +---------------------+
| |
v v
+---------------------+ +---------------------+
| Access Token Expiry| | Refresh Token |
| (Client Requests | | Sent to Server |
| New Access Token | | (Request New Token)|
+---------------------+ +---------------------+
| |
v v
+---------------------+ +---------------------+
| New Access Token | <-- | Refresh Token Valid|
+---------------------+ +---------------------+
|
v
Repeat Process
Token Storage Best Practices
Proper storage of tokens is essential to prevent unauthorized access. Here’s how to handle Access Tokens and Refresh Tokens securely.
1. Access Token Storage:
Memory: In client-side JavaScript, store the Access Token in memory (
sessionStorage
orlocalStorage
), not as a global variable.Short-lived: Due to its short lifespan, you don’t need to store it permanently.
2. Refresh Token Storage:
HttpOnly Cookies: Store Refresh Tokens securely in HttpOnly cookies, preventing access from JavaScript and protecting against XSS attacks.
Secure Storage: In mobile apps, store Refresh Tokens in secure storage like Keychain (iOS) or Keystore (Android).
// After successful login
res.cookie('refreshToken', refreshToken, {
httpOnly: true,
secure: true, // only for https
maxAge: 24 * 60 * 60 * 1000 // 1 day
});
// Send access token in response body
res.json({ accessToken: accessToken });
Security Considerations
Why Short Expiration for Access Tokens?
Access Tokens should expire quickly to reduce the potential for misuse if they are leaked. This minimizes the risk of long-term unauthorized access.
Protecting Tokens from Leaks
Always use HTTPS: This ensures that tokens are never transmitted in plaintext.
Never expose Refresh Tokens to JavaScript: Store them in HttpOnly cookies to prevent access via XSS attacks.
Token Revocation
Access Token Revocation: This is usually handled at the server level by blacklisting expired or compromised tokens.
Refresh Token Revocation: When a user logs out, or a token is compromised, invalidate the associated Refresh Token to prevent new Access Tokens from being issued.
Using HTTPS
Ensure your entire application communicates over HTTPS to encrypt data during transit.
Preventing XSS and CSRF Attacks
XSS (Cross-site Scripting): Ensure that tokens are not stored in locations accessible by JavaScript (e.g.,
localStorage
orsessionStorage
).CSRF (Cross-Site Request Forgery): Implement SameSite cookie attribute to prevent malicious websites from sending unauthorized requests.
Example: Node.js Implementation of Access & Refresh Tokens
Here’s a simple implementation in Node.js with the express library to demonstrate how to issue and refresh tokens.
Dependencies
npm install express jsonwebtoken dotenv
Code Snippet: Node.js Example
const express = require('express');
const jwt = require('jsonwebtoken');
const dotenv = require('dotenv');
dotenv.config();
const app = express();
app.use(express.json());
// Secret keys
const ACCESS_TOKEN_SECRET = process.env.ACCESS_TOKEN_SECRET;
const REFRESH_TOKEN_SECRET = process.env.REFRESH_TOKEN_SECRET;
// Sample user
const user = { id: 1, username: 'testuser' };
// Generate Access Token
function generateAccessToken(user) {
return jwt.sign(user, ACCESS_TOKEN_SECRET, { expiresIn: '15m' });
}
// Generate Refresh Token
function generateRefreshToken(user) {
return jwt.sign(user, REFRESH_TOKEN_SECRET);
}
// Login Endpoint
app.post('/login', (req, res) => {
// In a real app, you'd authenticate the user here
// Generate tokens
const accessToken = generateAccessToken(user);
const refreshToken = generateRefreshToken(user);
// Send tokens to the client
res.json({ accessToken, refreshToken });
});
// Refresh Token Endpoint
app.post('/token', (req, res) => {
const refreshToken = req.body.refreshToken;
if (!refreshToken) return res.status(401).send('Refresh Token is required');
jwt.verify(refreshToken, REFRESH_TOKEN_SECRET, (err, user) => {
if (err) return res.status(403).send('Invalid Refresh Token');
const newAccessToken = generateAccessToken({ id: user.id, username: user.username });
res.json({ accessToken: newAccessToken });
});
});
// Protected Route
app.get('/dashboard', authenticateToken, (req, res) => {
res.send('Welcome to your dashboard!');
});
// Middleware to authenticate Access Token
function authenticateToken(req, res, next) {
const token = req.header('Authorization') && req.header('Authorization').split(' ')[1];
if (!token) return res.status(401).send('Access Denied');
jwt.verify(token, ACCESS_TOKEN_SECRET, (err, user) => {
if (err) return res.status(403).send('Invalid Token');
req.user = user;
next();
});
}
app.listen(3000, () => {
console.log('Server running on http://localhost:3000');
});
Conclusion
Access Tokens and Refresh Tokens are essential in modern authentication systems. They help manage secure and efficient user sessions, especially in scenarios where multiple applications or services need to authenticate users.
Best Practices:
Always store tokens securely.
Implement short expiration for Access Tokens and long expiration for Refresh Tokens.
Use Refresh Tokens securely to avoid unnecessary login prompts.
Subscribe to my newsletter
Read articles from Muhammad Arqam Ejaz directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Muhammad Arqam Ejaz
Muhammad Arqam Ejaz
I'm Muhammad Arqam Ejaz A Pakistani Full Stack Developer I'm a passionate Software Engineer with 5+ years of experience in developing scalable web applications usingJavaScript, PHP, Laravel, React.js, and Node.js. Proficient in database optimization, DevOps, and cloud services (AWS, Firebase). Strong expertise in API integrations, e-commerce solutions (Shopify, WordPress), and Agile methodologies. Proven ability to translate complex business needs into efficient technical solutions. Skilled in writing clean, maintainable code. Having Strong leadership, problem-solving, and communication skills. About my educational background, I have done BS Software Engineering Gift University, Gujranwala, Pakistan