Гайды / Работа с JWT
В этом разделе рассмотрим, как правильно работать с JWT (access/refresh токенами) в Nuxoblivius. Мы разберём, как:
- не привязываться жёстко к таймаутам access-токена;
- автоматически обновлять access при его истечении;
- настраивать запросы так, чтобы даже при ошибках они корректно выполнялись;
- сделать всё это без проблем в SSR.
Основная идея
Вместо того чтобы полагаться на фиксированный таймер истечения токена, лучше отлавливать реальный ответ сервера. Если access оказался просрочен, запрос автоматически прерывается и запускается процесс получения нового access через refresh.
После обновления токена:
- исходный запрос повторяется;
- пользователь продолжает работать без разрыва сессии.
Подготовка
Для работы с JWT создадим отдельный стор AuthService, в котором будут два основных запроса:
login— авторизация пользователя и получение access/refresh токенов;
refresh— обновление access токена при его истечении.
import { defineSingleton, Record, SetDefaultAuth } from "nuxoblivius";
class AuthService {
private _accessToken?: string;
private _refreshToken?: string;
private _loginRecord = Record.new("/api/login", {})
.header("Content-Type", "application/json")
.onFailure(() => {});
private _refreshRecord = Record.new("/api/refresh", {})
.header("Content-Type", "application/json")
.onFailure(() => {});
public getAccessToken(): string {
return this._accessToken;
}
public async login(loginForm: object) {
const responseData = await this._loginRecord.post(loginForm);
if (this._loginRecord.error) {
return new Error("Uncorrect credentials");
}
this._accessToken = responseData.access;
this._refreshToken = responseData.refresh;
SetDefaultAuth(Record.Bearer(this._accessToken));
}
public async refresh() {
if (!this._refreshToken) {
throw new Error("User not logined");
}
const responseData = await this._refreshRecord.post({
refreshToken: this._refreshToken,
});
if (this._refreshRecord.error) {
return new Error("Failed to refresh auth");
}
this._accessToken = responseData.access;
this._refreshToken = responseData.refresh;
SetDefaultAuth(Record.Bearer(this._accessToken));
}
}
export default defineSingleton(AuthService);Настройка глобального .onFailure() хука
В Nuxoblivius можно настроить глобальный перехват ошибок, чтобы одинаково обрабатывать их для всех запросов. Это особенно полезно при работе с JWT.
Самый частый сценарий — перехват 401 Unauthorized. В этом случае мы можем:
- Проверить, есть ли у нас refresh токен.
- Если есть — выполнить запрос обновления.
- После успешного обновления — повторить исходный запрос.
Таким образом, пользователю не придётся вручную перелогиниваться, если access токен истёк.
Для этого используется функция SetRequestFailure(). Она работает аналогично .onFailure(), но применяется ко всем запросам в приложении.
import { SetRequestFailure } from "nuxoblivius";
import AuthService from "@/stores/AuthStore";
SetRequestFailure(async (info, retry) => {
// Проверяем, что ошибка — это 401
if (info.code === 401) {
// Пытаемся обновить токен
await AuthService.refresh();
// После успешного обновления пробуем запрос заново
return retry();
}
});import { SetRequestFailure, tryAbortAllRequest } from "nuxoblivius";
import AuthService from "@/stores/AuthStore";
SetRequestFailure(async (info, retry) => {
// Проверяем, что ошибка — это 401
if (info.code === 401 || info.abortCode === 401) {
// Прерываем все запросы
tryAbortAllRequest(401);
// Пытаемся обновить токен
await AuthService.refresh();
// После успешного обновления пробуем запрос заново
return retry();
}
});Итого
Глобальный .onFailure() хук через SetRequestFailure() позволяет централизованно обрабатывать ошибки всех запросов. Наиболее полезный сценарий — это автоматическое обновление access токена при получении статуса 401. В этом случае мы проверяем наличие refresh токена, выполняем обновление, и повторяем запрос заново.
