0
0
ExpressHow-ToBeginner · 4 min read

How to Use Refresh Token in Express for Secure Authentication

In Express, use refresh tokens to issue new access tokens when the old ones expire without forcing users to log in again. Store refresh tokens securely (e.g., HTTP-only cookies) and create an endpoint to verify the refresh token and send a new access token.
📐

Syntax

The typical flow involves these parts:

  • Access Token: Short-lived token for API access.
  • Refresh Token: Long-lived token to get new access tokens.
  • Refresh Endpoint: An Express route that accepts a refresh token, verifies it, and returns a new access token.
  • Token Storage: Store refresh tokens securely, usually in HTTP-only cookies or a database.
javascript
app.post('/refresh-token', (req, res) => {
  const refreshToken = req.cookies.refreshToken;
  if (!refreshToken) return res.sendStatus(401);

  jwt.verify(refreshToken, process.env.REFRESH_TOKEN_SECRET, (err, user) => {
    if (err) return res.sendStatus(403);
    const accessToken = jwt.sign({ username: user.username }, process.env.ACCESS_TOKEN_SECRET, { expiresIn: '15m' });
    res.json({ accessToken });
  });
});
💻

Example

This example shows a simple Express server that issues access and refresh tokens on login, stores the refresh token in a cookie, and provides a refresh endpoint to get new access tokens.

javascript
import express from 'express';
import jwt from 'jsonwebtoken';
import cookieParser from 'cookie-parser';

const app = express();
app.use(express.json());
app.use(cookieParser());

const users = [{ username: 'user1', password: 'pass1' }];

app.post('/login', (req, res) => {
  const { username, password } = req.body;
  const user = users.find(u => u.username === username && u.password === password);
  if (!user) return res.status(401).send('Invalid credentials');

  const accessToken = jwt.sign({ username }, process.env.ACCESS_TOKEN_SECRET, { expiresIn: '15m' });
  const refreshToken = jwt.sign({ username }, process.env.REFRESH_TOKEN_SECRET, { expiresIn: '7d' });

  res.cookie('refreshToken', refreshToken, { httpOnly: true, secure: true, sameSite: 'strict' });
  res.json({ accessToken });
});

app.post('/refresh-token', (req, res) => {
  const refreshToken = req.cookies.refreshToken;
  if (!refreshToken) return res.sendStatus(401);

  jwt.verify(refreshToken, process.env.REFRESH_TOKEN_SECRET, (err, user) => {
    if (err) return res.sendStatus(403);
    const accessToken = jwt.sign({ username: user.username }, process.env.ACCESS_TOKEN_SECRET, { expiresIn: '15m' });
    res.json({ accessToken });
  });
});

app.listen(3000, () => console.log('Server running on http://localhost:3000'));
Output
Server running on http://localhost:3000
⚠️

Common Pitfalls

Common mistakes when using refresh tokens in Express include:

  • Storing refresh tokens in localStorage or accessible JavaScript storage, risking XSS attacks.
  • Not verifying the refresh token properly before issuing a new access token.
  • Not handling token expiration or revocation, which can allow unauthorized access.
  • Sending refresh tokens in URL parameters, exposing them in logs or browser history.

Always use httpOnly cookies for refresh tokens and verify tokens with proper secrets.

javascript
/* Wrong way: sending refresh token in request body without verification */
app.post('/refresh-token', (req, res) => {
  const { refreshToken } = req.body;
  // Missing verification step
  const accessToken = jwt.sign({ username: 'user1' }, process.env.ACCESS_TOKEN_SECRET, { expiresIn: '15m' });
  res.json({ accessToken });
});

/* Right way: verify refresh token before issuing new access token */
app.post('/refresh-token', (req, res) => {
  const refreshToken = req.cookies.refreshToken;
  if (!refreshToken) return res.sendStatus(401);

  jwt.verify(refreshToken, process.env.REFRESH_TOKEN_SECRET, (err, user) => {
    if (err) return res.sendStatus(403);
    const accessToken = jwt.sign({ username: user.username }, process.env.ACCESS_TOKEN_SECRET, { expiresIn: '15m' });
    res.json({ accessToken });
  });
});
📊

Quick Reference

  • Access Token: Short life, sent in Authorization header.
  • Refresh Token: Long life, stored in httpOnly cookie.
  • Refresh Endpoint: Verifies refresh token and returns new access token.
  • Security: Use HTTPS, httpOnly cookies, and verify tokens carefully.

Key Takeaways

Always verify refresh tokens before issuing new access tokens in Express.
Store refresh tokens securely using httpOnly cookies to prevent XSS attacks.
Use short-lived access tokens and long-lived refresh tokens for better security.
Create a dedicated refresh endpoint to handle token renewal safely.
Never expose refresh tokens in URLs or client-accessible storage.