What is Silent Refresh and How to Implement It
- 749Words
- 4Minutes
- 09 Aug, 2024
Silent refresh is a mechanism that allows frontend applications to automatically request a new token when the current token is about to expire, without requiring user intervention. This approach enhances user experience by avoiding login interruptions due to token expiration.
Common Methods to Implement Silent Refresh
Token Expiry Management
- After user login, the frontend typically stores access tokens (e.g., JWT) and refresh tokens.
- Access tokens usually have a short validity period (e.g., 15 minutes), while refresh tokens have a longer validity period (e.g., 7 days).
- The frontend proactively uses the refresh token to request a new access token from the backend when the access token is about to expire (e.g., 2 minutes remaining).
Interceptors
- Frontend applications can use interceptors (e.g., in Axios or Fetch) to intercept all requests.
- If the current access token is detected as expired, the interceptor first uses the refresh token to obtain a new access token, then resends the original request.
- This ensures that users do not perceive any interruption, and the application maintains continuous operation.
Silent Refresh Logic
- Set up a timer to automatically trigger a refresh operation before the token expires.
- Regularly check the token’s validity, and automatically refresh it if it is about to expire to prevent user operations from being interrupted.
- If the refresh token also expires, redirect the user to the login page.
Silent Authentication
- For some frontend applications, silent authentication can be performed using a hidden iframe to complete token refresh in the background using SSO mechanisms, without affecting the user’s frontend operations.
Example of Axios Interceptor Implementation
Here is a complete code example of an Axios interceptor to handle JWT token auto-refresh and request retries. In this example, it is assumed that you already have a backend API that provides token refresh functionality.
1. Create Axios Instance
First, create an Axios instance and configure request and response interceptors.
1import axios from "axios";2
3// Create Axios instance4const apiClient = axios.create({5 baseURL: "https://api.example.com", // Replace with your API base URL6 timeout: 10000,7});
2. Add Request Interceptor
Include the Authorization header with each request to attach the JWT access token.
1// Add request interceptor2apiClient.interceptors.request.use(3 (config) => {4 // Add Authorization header with each request5 const token = localStorage.getItem("token");6 if (token) {7 config.headers["Authorization"] = `Bearer ${token}`;8 }9 return config;10 },11 (error) => {12 // Handle request error13 return Promise.reject(error);14 },15);
3. Add Response Interceptor
Handle 401 errors and use the refresh token to obtain a new access token if the token has expired.
1// Add response interceptor2apiClient.interceptors.response.use(3 (response) => {4 // Directly return the response data5 return response;6 },7 async (error) => {8 const originalRequest = error.config;9
10 // If response status code is 401 and original request has not been retried11 if (12 error.response &&13 error.response.status === 401 &&14 !originalRequest._retry15 ) {16 originalRequest._retry = true;17 try {18 const refreshToken = localStorage.getItem("refreshToken");19 if (refreshToken) {20 // Send refresh token request21 const { data } = await axios.post(22 "https://api.example.com/auth/refresh",23 { token: refreshToken },24 );25
26 // Update token in local storage27 localStorage.setItem("token", data.token);28
29 // Update Authorization header30 axios.defaults.headers.common["Authorization"] =31 `Bearer ${data.token}`;32
33 // Resend original request34 originalRequest.headers["Authorization"] = `Bearer ${data.token}`;35 return apiClient(originalRequest);36 }37 } catch (refreshError) {38 // If refresh token also fails, redirect to login page39 localStorage.removeItem("token");40 localStorage.removeItem("refreshToken");41 window.location.href = "/login";42 }43 }44
45 // Handle other errors46 return Promise.reject(error);47 },48);49
50export default apiClient;
Code Explanation
-
Axios Instantiation:
Using axios.create() to create a custom Axios instance
apiClient
, specifying the base URL and other configurations. -
Request Interceptor:
Before each request, check if a JWT access token exists and, if so, add it to the Authorization header.
-
Response Interceptor:
- Handle 401 Errors: When the server returns a 401 Unauthorized error, the token may have expired.
- Refresh Token: If a refresh token exists, send a request to the refresh token API endpoint to get a new access token.
- Update Token: Store the new token in local storage and update the default request headers in the Axios instance for subsequent requests.
- Retry Original Request: Resend the original request with the new token.
-
Refresh Token Failure:
If the refresh token is invalid or expired, clear the tokens from local storage and redirect the user to the login page.
How to Use
In your application, you can use apiClient
instead of the original Axios instance to send HTTP requests:
1import apiClient from "./apiClient";2
3async function getUserData() {4 try {5 const response = await apiClient.get("/user/profile");6 console.log(response.data);7 } catch (error) {8 console.error("Failed to get user data:", error);9 }10}
This code uses the encapsulated apiClient
to send requests, automatically handling token refresh and retry logic.
Summary
By implementing silent refresh, you can ensure that users are not interrupted by token expiration during their use of the application. This mechanism enhances user experience and makes the application more seamless.