Refresh tokens allows you to issue short-lived access tokens and longer-lived refresh tokens. When the access token expires, the client can send the refresh token to get a new access token. This provides an additional layer of control for logout or token expiration.
- Access Token: Short-lived (e.g., 15 minutes), used to access protected resources.
- Refresh Token: Long-lived (e.g., 30 days), used to request new access tokens after expiration.
Refresh Token Flow:
- 
User Logs In: 
- The server sends both an access token (JWT) and a refresh token.
 
- 
User Logs Out: 
- The client removes both the access token and refresh token.
- The server can invalidate the refresh token (e.g., remove it from the database).
 
- 
Access Token Expiry: 
- When the access token expires, the client can use the refresh token to obtain a new access token from the server.
 
This approach allows for more robust logout functionality, as refresh tokens can be invalidated server-side, ensuring that the user can't continue using old tokens.
2. Why Use Refresh Tokens?
Refresh tokens help you separate concerns of authentication (verifying a user's identity) and session management (keeping a user logged in over time) while maintaining security.
Key Benefits:
- 
Short-Lived Access Tokens (Access Control): - If the access token is short-lived (e.g., 15 minutes), it limits the exposure time of an access token.
- If an attacker steals the access token, the damage is limited by its short lifespan.
 
- 
Long-Lived Refresh Tokens (Reauthentication): - Refresh tokens allow the user to stay logged in without needing to authenticate again after the access token expires.
- Refresh tokens are kept securely (on the client side, usually in an httpOnly cookie), and only sent to the server when a new access token is needed.
 
3. How the Client Handles Refresh Tokens
Here's the flow for handling refresh tokens on the client side, assuming you're using JWTs.
Login Flow:
- User logs in with their credentials (email + password).
- 
The server responds with: - Access Token (short-lived, e.g., 15 minutes)
- Refresh Token (long-lived, e.g., 30 days)
 
- 
The client stores: - The access token (e.g., in memory, localStorage, or a cookie for API calls).
- The refresh token (preferably in an httpOnly cookie, which is less vulnerable to XSS attacks).
 
Requesting New Access Token with Refresh Token:
- When the client makes a request to a protected route, it includes the access token in the Authorizationheader.
- If the access token expires, the client will send the refresh token to the server to get a new access token:
- The server verifies the refresh token.
- If valid, the server issues a new access token and sends it back to the client.
- The server may also refresh the refresh token itself, issuing a new one with each access token renewal (to prevent long-term use of an old refresh token).
 
Logout Flow:
- To log out, the client can:
- Remove both the access token and refresh token from the client (i.e., from localStorage or cookies).
- Optionally, the client can notify the server to invalidate the refresh token (by blacklisting or removing it from the database).
 
4. How to Implement Refresh Tokens on the Client-Side
Hereโs how you would manage the refresh token on the client side, using localStorage, sessionStorage, or httpOnly cookies.
1. Storing the Tokens
- 
Access Token: - You may store it in localStorage or sessionStorage to make it easily available for API requests. However, this could expose the token to XSS attacks if the site is vulnerable.
- Alternatively, you can store it in memory (in a JS variable) while the app is running (but this means the user will need to log in again if the page is refreshed).
 
- 
Refresh Token: - httpOnly cookies are the most secure option for storing refresh tokens, as it prevents access to the refresh token via JavaScript (protecting from XSS attacks).
- Example: Set-Cookie: refreshToken=<value>; HttpOnly; Secure; SameSite=Strict.
 
2. Making API Requests with the Access Token
For each request to a protected route, you send the access token in the Authorization header:
fetch('https://api.example.com/protected', {
  method: 'GET',
  headers: {
    'Authorization': `Bearer ${accessToken}`, // Attach the access token
  },
})
.then(response => response.json())
.then(data => {
  // Handle the API response
})
.catch(error => {
  // Handle errors
});
3. Handling Access Token Expiry and Refreshing
If the access token expires, you can request a new one by using the refresh token.
- First, check if the access token has expired.
- If expired, send a request to your backend to refresh the token.
Example refresh token request:
// Function to get a new access token using refresh token
function refreshAccessToken() {
  return fetch('https://api.example.com/refresh', {
    method: 'POST',
    body: JSON.stringify({ refreshToken }),
    headers: {
      'Content-Type': 'application/json',
    },
  })
  .then(response => response.json())
  .then(data => {
    const { accessToken, refreshToken: newRefreshToken } = data;
    // Store the new tokens (in memory, localStorage, etc.)
    storeTokens(accessToken, newRefreshToken);
  })
  .catch(error => {
    // Handle error, e.g., if refresh token is invalid or expired
  });
}
4. Removing Tokens on Logout
To log out, you simply remove both the access token and refresh token from wherever they are stored:
// Remove from localStorage
localStorage.removeItem('accessToken');
localStorage.removeItem('refreshToken');
// Remove from cookies
document.cookie = 'refreshToken=; Max-Age=0; path=/;';
5. Server-Side: Handling Refresh Tokens
When the client sends a refresh token to get a new access token, the server will:
- Verify the refresh token (check its signature, expiration, etc.).
- If valid:
- Issue a new access token.
- Optionally issue a new refresh token.
- If the refresh token is expired or invalid, deny the request and require the user to log in again.
 
Example Express Route for Refreshing Tokens:
// src/routes/refreshTokenRoute.ts
import express from 'express';
import { verifyRefreshToken, generateAccessToken } from '../utils/jwtUtils';
import { getRefreshTokenFromCookies } from '../utils/cookieUtils';
const router = express.Router();
router.post('/refresh', async (req, res) => {
  const refreshToken = getRefreshTokenFromCookies(req);
  if (!refreshToken) {
    return res.status(401).json({ message: 'Refresh token missing' });
  }
  try {
    const user = await verifyRefreshToken(refreshToken); // Verify refresh token
    const newAccessToken = generateAccessToken(user.id); // Generate a new access token
    const newRefreshToken = generateRefreshToken(user.id); // Optionally, generate a new refresh token
    res.json({
      accessToken: newAccessToken,
      refreshToken: newRefreshToken, // Optionally send the new refresh token
    });
  } catch (error) {
    res.status(403).json({ message: 'Invalid refresh token' });
  }
});
export default router;
Conclusion
Refresh tokens provide a more secure and flexible approach for managing long-lived user sessions in a stateless authentication system. They allow you to:
- Keep access tokens short-lived for security (limiting exposure if the token is stolen).
- Use refresh tokens to request new access tokens without requiring the user to log in repeatedly.
- Ensure logout by simply removing both tokens from the client-side storage.
This separation between access tokens and refresh tokens gives you more control over security and user experience.
What are your thoughts๐?
 

 
    
Top comments (0)