In React (or JavaScript in general), interceptors are often used to intercept and modify requests or responses in HTTP communication. They are commonly implemented in libraries like Axios, which is used for handling HTTP requests.
Here’s a brief explanation of the interceptor concept in React:
1. What is an Interceptor?
An interceptor is a function that acts as middleware, intercepting HTTP requests before they are sent to the server, or responses before they reach the application. You can use interceptors to:
- Modify headers (e.g., adding authentication tokens).
- Handle error responses globally (e.g., redirect to login on 401).
- Log requests and responses.
- Retry failed requests.
2. How it Works in Axios:
In Axios, you can set up interceptors for both requests and responses. When a request or response is intercepted, it can be modified, logged, or handled based on custom logic.
Simple Approach for Refresh Token Handling:
- Store Tokens in Local Storage: Use
localStorage
orsessionStorage
to store the access and refresh tokens. - Request Interceptor: Add the access token to every request’s headers.
- Response Interceptor: If the API returns a
401 Unauthorized
error, automatically use the refresh token to get a new access token and retry the failed request.
Step-by-Step Code:
1. Axios Setup with Interceptors
import axios from 'axios';
// Axios instance
const api = axios.create({
baseURL: 'https://api.yourdomain.com', // API base URL
});
// Function to get access token from localStorage
const getAccessToken = () => localStorage.getItem('accessToken');
// Function to get refresh token from localStorage
const getRefreshToken = () => localStorage.getItem('refreshToken');
// Request interceptor: Attach access token to every request
api.interceptors.request.use(
(config) => {
const token = getAccessToken();
if (token) {
config.headers['Authorization'] = `Bearer ${token}`;
}
return config;
},
(error) => Promise.reject(error)
);
2. Simplified Token Refresh Logic
// Response interceptor: Handle 401 errors and refresh token
api.interceptors.response.use(
(response) => response, // Pass successful responses through
async (error) => {
const originalRequest = error.config;
// If 401 error and not retrying already
if (error.response.status === 401 && !originalRequest._retry) {
originalRequest._retry = true; // Set retry flag
// Attempt to refresh the token
try {
const refreshToken = getRefreshToken();
const { data } = await axios.post('https://api.yourdomain.com/refresh-token', {
refreshToken: refreshToken,
});
// Store the new access token
localStorage.setItem('accessToken', data.accessToken);
// Retry the original request with new token
originalRequest.headers['Authorization'] = `Bearer ${data.accessToken}`;
return api(originalRequest);
} catch (refreshError) {
// Handle refresh token failure (e.g., log out user)
console.error('Refresh token failed', refreshError);
localStorage.removeItem('accessToken');
localStorage.removeItem('refreshToken');
window.location.href = '/login'; // Redirect to login page
return Promise.reject(refreshError);
}
}
return Promise.reject(error); // For other errors
}
);
How it Works:
- Access Token Storage: The access and refresh tokens are stored in
localStorage
. - Request Interceptor: Before each request, the
Authorization
header is set with the access token fromlocalStorage
. - Response Interceptor: When a
401 Unauthorized
error is encountered:
- The refresh token is sent to the server to get a new access token.
- If successful, the new access token is saved, and the original request is retried with the new token.
- If the refresh token is invalid or expired, the user is redirected to the login page.
Why This is Easier:
- No Complex Queuing: This example skips queuing multiple failed requests while the token refresh is in progress.
- Single Responsibility: Each interceptor handles a specific task — either attaching the token or handling the error.
- Minimal Error Handling: If the refresh fails, it logs out the user and forces re-authentication.
Additional Enhancements:
- You can store the tokens more securely (e.g., use HTTP-only cookies).
- Handle token expiration proactively by checking token expiration times rather than waiting for
401
errors.
Conclusion:
This approach simplifies the process of handling expired tokens with interceptors while still ensuring a smooth experience for the user. It removes the need for advanced queuing logic and focuses on basic token storage and refreshing when needed.