In modern web applications, secure authentication is crucial to protect user data and ensure a smooth login experience. Two critical components of authentication are Access Tokens and Refresh Tokens. These tokens work together to verify user identity, maintain session security, and reduce the need for repeated logins. In this article, weβll explore what they are, how they function, and best practices for using them effectively.
πΉ What is an Access Token?
An Access Token is a short-lived credential used to authenticate a userβs request to access a protected resource, such as an API or a web service. It is usually issued by an authentication server when a user logs in and must be included in the request headers for secure communication.
β Key Features of an Access Token
Short lifespan (e.g., 15 minutes to 1 hour)
Encoded using JWT (JSON Web Token) format
Contains user information (e.g., user ID, role, permissions)
Sent with every API request in the Authorization header
Cannot be refreshed once expired; requires re-authentication or a refresh token
π Example: Decoded Access Token (JWT)
{
"_id": "user123",
"role": "admin",
"iat": 1700000000, // Issued at
"exp": 1700000900 // Expires in 15 minutes
}
π What is a Refresh Token?
A Refresh Token is a long-lived token used to generate a new Access Token without requiring the user to log in again. Since Access Tokens expire quickly, Refresh Tokens ensure users do not need to authenticate frequently while maintaining security.
β Key Features of a Refresh Token
Longer lifespan (e.g., 7 to 30 days)
Used only to obtain a new Access Token
Stored securely (e.g., HTTP-only cookies, database)
Cannot be used to access APIs directly
Can be revoked if necessary (e.g., user logs out or session is invalidated)
π Example: Decoded Refresh Token (JWT)
{
"_id": "user123",
"iat": 1700000000,
"exp": 1700864000 // Expires in 7 days
}
β‘ How Do Access and Refresh Tokens Work Together?
1οΈβ£ User logs in β Server issues an Access Token and a Refresh Token
2οΈβ£ User makes API requests β Sends Access Token in the Authorization
header
3οΈβ£ Access Token expires β User sends Refresh Token to request a new one
4οΈβ£ If Refresh Token is valid β Server issues a new Access Token
5οΈβ£ If Refresh Token is invalid or expired β User must log in again
π Implementing Access & Refresh Tokens in Node.js
πΉ Step 1: Install Required Packages
npm install jsonwebtoken dotenv express cookie-parser
πΉ Step 2: Configure Environment Variables (.env
)
ACCESS_TOKEN_SECRET=secret
ACCESS_TOKEN_EXPIRY=1d
REFRESH_TOKEN_SECRET=secret
REFRESH_TOKEN_EXPIRY=10d
πΉ Step 3: Generate Tokens
import jwt from "jsonwebtoken";
import dotenv from "dotenv";
dotenv.config({ path: './.env' });
generateAccessToken = function ( userId, email ) {
return jwt.sign(
{
_id: userId,
email: email,
},
process.env.ACCESS_TOKEN_SECRET,
{
expiresIn: process.env.ACCESS_TOKEN_EXPIRY
}
)
}
generateRefreshToken = function ( userId ) {
return jwt.sign(
{
_id: userId,
},
process.env.REFRESH_TOKEN_SECRET,
{
expiresIn: process.env.REFRESH_TOKEN_EXPIRY
}
)
}
export {
generateAccessToken,
generateRefreshToken,
}
πΉ Step 4: Authenticate Users
const loginUser = (req, res) => {
const { email, password } = req.body;
const user = { _id: "user123", email: "test@example.com" };
if (!user) return res.status(401).json({ message: "Invalid credentials" });
const accessToken = generateAccessToken(user._id, user.email );
const refreshToken = generateRefreshToken(user._id);
const options = { httpOnly: true, secure: true, sameSite: "Strict" }
res
.cookie("refreshToken", refreshToken, options);
.json({ accessToken });
};
πΉ Step 5: Middleware for Protecting Routes
const authenticateToken = (req, res, next) => {
const token = req.header("Authorization")?.split(" ")[1];
if (!token) return res.status(401).json({ message: "Access Denied" });
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
next();
} catch (error) {
res.status(403).json({ message: "Invalid Token" });
}
};
export default authenticateToken;
πΉ Step 6: Refreshing the Access Token
const refreshAccessToken = (req, res) => {
const { refreshToken } = req.cookies;
if (!refreshToken) return res.status(401).json({ message: "No token provided" });
jwt.verify(refreshToken, process.env.REFRESH_TOKEN_SECRET, (err, user) => {
if (err) return res.status(403).json({ message: "Invalid refresh token" });
const newAccessToken = generateAccessToken(user._id)
res.json({ accessToken: newAccessToken });
});
};
πΉ Step 7: Logging Out Securely
const logoutUser = (req, res) => {
res.clearCookie("refreshToken");
res.json({ message: "Logged out successfully" });
};
π Best Security Practices
β Use HTTPS to prevent token interception
β Store Refresh Tokens securely (HTTP-only, Secure cookies)
β Use short expiry for Access Tokens (15-30 minutes)
β Rotate Refresh Tokens to enhance security
β Blacklist compromised Refresh Tokens in a database
π Conclusion
Access Tokens and Refresh Tokens work together to create a secure authentication system. While the Access Token ensures quick API access, the Refresh Token extends the session without requiring frequent logins. Implementing them correctly enhances security, improves user experience, and reduces authentication overhead.
By following best practices, you can effectively protect your users while providing seamless authentication. Let us know if you have any questions or need further guidance! ππ