Приветствую, дорогой читатель! Хочу поделиться решением наболевшей проблемы — автоматическим обновлением access-токена при истечении его срока действия.
Эталонный сценарий
Пользователь успешно авторизуется, после чего access- и refresh-токены сохраняются в куки.
Время жизни access token заканчивается, нужно запросить новый с использованием refresh.
Перед отправкой запроса, требующего acess-токен, проверяем его наличие. Если его нет, отправляем запрос на получение нового.
Пример такой реализации:
api.interceptors.request.use(
async (config: AxiosRequestConfig) => {
let accessToken = getCookie('accessToken');
if (!accessToken) {
accessToken = await refreshAccessToken();
}
if (accessToken && config.headers) {
config.headers.Authorization = `Bearer ${accessToken}`;
}
return config;
},
(error: AxiosError) => {
return Promise.reject(error);
}
);
const fetchUser = async () => {
const response = await api.get('/user');
return response.data;
}
На первый взгляд всё верно, и такой код действительно работает. Но представьте ситуацию: access-токен устарел, и пользователь переходит на новую страницу, где отправляется несколько запросов, требующих токена. Поскольку токен отсутствует, каждый запрос попытается обновить его, не зная, что другой запрос уже это делает.
Правильный сценарий
Вернёмся к моменту, когда мы проверяем наличие токена:
if (!accessToken) {
accessToken = await refreshAccessToken();
}
Воспользуемся библиотекой axios-jwt-refresh-token
.
Создать флаг, который будет указывать, запрашивается ли сейчас новый access-токен.
Организовать очередь запросов, требующих access-токена. Останавливать их и ждать завершения получения нового токена.
После успешного получения токена достать запросы из очереди и продолжить их выполнение.
Как это реализовать?
Воспользуемся библиотекой axios-jwt-refresh-token. Установим необходимые пакеты:
npm install axios-jwt-refresh-token axios js-cookie
Настроим обработчик для Axios:
import axios from 'axios';
import { createTokenRefreshMiddleware } from 'axios-jwt-refresh-token';
// 1. Создаём экземпляр Axios
const axiosInstance = axios.create();
// 2. Функция для обновления токенов
const requestNewTokens = async () => {
const response = await axios.post('/auth/refresh');
// Важно вернуть объект в таком формате
return {
accessToken: response.data.access_token,
// Если refresh-токен не нужен, просто удалите эту строку
refreshToken: response.data.refresh_token
};
};
// 3. Создаём middleware для обработки токенов
const requestAccessMiddleware = createTokenRefreshMiddleware({
requestTokens: requestNewTokens,
onRefreshAndAccessExpire: () => {
// Обработка истёкшей сессии
window.location.href = '/login';
},
accessTokenKey: 'accessToken',
refreshTokenKey: 'refreshToken',
cookiesOptions: { secure: true, sameSite: 'strict' }
});
// 4. Подключаем middleware к Axios
axiosInstance.interceptors.request.use(requestAccessMiddleware);
Теперь при использовании этого экземпляра Axios вам не нужно вручную передавать access-токен — всё будет работать автоматически.
Contrabondo
Также можно и готовые адаптеры использовать, например для keycloak есть его готовый, с апдейтами и прочим:
https://www.npmjs.com/package/keycloak-js
А так смысл правильный. Иногда ещё бывает надо добавлять апдейты на активации окон/вкладок, а не только при запросах
artemmorozov13 Автор
Спасибо за замечание, забыл об этом, добавлю обработку в следующей версии