Refresh Token vs Access Token in Express Backend – Understanding the Differences

·

3 min read

Introduction

Authentication is a critical aspect of web applications, ensuring that only authorized users can access protected resources. In an Express.js backend, authentication is commonly handled using JWT (JSON Web Token), which provides a secure way to verify user identity. Two types of tokens play a key role in this process: Access Tokens and Refresh Tokens. While both are essential for maintaining authentication, they serve different purposes. Let’s explore their differences and implementation in an Express.js backend.

What is an Access Token?

An Access Token is a short-lived token used to authenticate API requests. It is typically generated upon user login and included in request headers to access protected resources.

Characteristics of an Access Token:

  • Short lifespan (usually a few minutes to hours).

  • Sent with API requests in the Authorization header.

  • Encodes user information (like user ID, role, etc.) in a compact and secure way.

  • Expires quickly, requiring re-authentication or refresh.

Example of Access Token Usage:

fetch('/api/protected-route', {
  method: 'GET',
  headers: {
    'Authorization': `Bearer <access_token>`
  }
});

What is a Refresh Token?

A Refresh Token is a long-lived token used to obtain a new access token when the current one expires. Instead of requiring the user to log in again, the refresh token helps maintain seamless authentication.

Characteristics of a Refresh Token:

  • Longer lifespan (can last days or weeks).

  • Stored securely (often in httpOnly cookies or a secure database).

  • Used only to obtain a new access token, not for direct API access.

  • Can be revoked if a user logs out or a security breach occurs.

Example of Refresh Token Usage:

fetch('/api/refresh-token', {
  method: 'POST',
  credentials: 'include'
})
.then(response => response.json())
.then(data => {
  localStorage.setItem('access_token', data.accessToken);
});

Key Differences Between Access Token and Refresh Token

FeatureAccess TokenRefresh Token
LifespanShort-lived (minutes/hours)Long-lived (days/weeks)
UsageSent with API requestsUsed to obtain a new access token
StorageMemory or localStoragehttpOnly cookies or database
Security RiskHigher if leakedLess risky but still needs protection

Implementing Access & Refresh Tokens in Express.js

1. Setting Up JWT Authentication

Install the required dependencies:

npm install express jsonwebtoken cookie-parser dotenv cors

2. Generating Access and Refresh Tokens

const jwt = require('jsonwebtoken');
const generateTokens = (user) => {
  const accessToken = jwt.sign({ id: user.id }, process.env.ACCESS_TOKEN_SECRET, { expiresIn: '15m' });
  const refreshToken = jwt.sign({ id: user.id }, process.env.REFRESH_TOKEN_SECRET, { expiresIn: '7d' });
  return { accessToken, refreshToken };
};

3. Protecting Routes with Access Tokens

const authenticateToken = (req, res, next) => {
  const token = req.headers['authorization']?.split(' ')[1];
  if (!token) return res.status(401).json({ message: 'Access Denied' });

  jwt.verify(token, process.env.ACCESS_TOKEN_SECRET, (err, user) => {
    if (err) return res.status(403).json({ message: 'Token Expired or Invalid' });
    req.user = user;
    next();
  });
};

4. Handling Token Refreshing

const refreshTokens = []; // Store refresh tokens securely

app.post('/api/refresh-token', (req, res) => {
  const { refreshToken } = req.body;
  if (!refreshToken || !refreshTokens.includes(refreshToken)) return res.status(403).json({ message: 'Invalid refresh token' });

  jwt.verify(refreshToken, process.env.REFRESH_TOKEN_SECRET, (err, user) => {
    if (err) return res.status(403).json({ message: 'Token Expired or Invalid' });
    const { accessToken, refreshToken: newRefreshToken } = generateTokens(user);
    res.json({ accessToken, refreshToken: newRefreshToken });
  });
});

Security Best Practices

  • Use httpOnly and Secure cookies to store refresh tokens.

  • Set token expiration times to minimize security risks.

  • Implement token revocation when a user logs out.

  • Use HTTPS to encrypt tokens in transit.

  • Prevent XSS & CSRF attacks by sanitizing inputs and using CSRF protection.

Conclusion

Understanding the difference between access and refresh tokens is crucial for building a secure authentication system in Express.js. Access tokens provide quick authentication but expire fast, while refresh tokens help extend user sessions securely. By implementing both correctly, developers can enhance user experience while maintaining high security.