Angular: Запрос на получение нового токена через интерсептор
Есть сервер который после аутентификации отдает 2 токена access и refresh. На клиенте я получаю их и ложу в local storage. Я выковал интерсептор который к каждому запросу вешает access token из local storage. Проблема в том что когда время access токена истекает, мне прилетает естественная ошибка 403. Я бы хотел найти способ в интерсепторе после отправки запроса наблюдать за ответом и сделать запрос на получение новых токенов при возникновении данной ошибки.
Сейчас имеется такой код:
Service
login(formData: FormData): Observable<any> {
return this.httpClient.post<any>(`${this.apiBaseUrl}/login`, formData)
.pipe(
tap(response => {
localStorage.setItem('access_token', response['access_token']);
localStorage.setItem('refresh_token', response['refresh_token']);
})
);
}
Interceptor
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if(!!localStorage.getItem('access_token')) {
const clone = req.clone({
headers: req.headers.append('Authorization',
`Bearer ${localStorage.getItem('access_token')}`)
});
return next.handle(clone); // Здесь нужно обработать 403 ошибку
// 1. Делать новый запрос к серваку на получение токенов
// 2. Повторить логику метода
}
return next.handle(req);
}
Провальная попытка:
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if(!!localStorage.getItem('access_token')) {
const clone = req.clone({
headers: req.headers.append('Authorization',
`Bearer ${localStorage.getItem('access_token')}`)
});
return next.handle(clone).pipe(
tap((event: HttpResponse<any>) => {
if(event.type == 0) {
// Так наверное делать нельзя
this.httpClient.get(`${this.apiBaseUrl}/token/refresh`, {
headers: {
'Authorization': `Bearer ${localStorage.getItem('refresh_token')}`
}
})
// Call stack error, а как иначе?))
.subscribe(response => {
localStorage.setItem('access_token', response['access_token']);
localStorage.setItem('refresh_token', response['refresh_token']);
}
);
}
}),
);
}
return next.handle(req);
}
Ответы (1 шт):
Вам необходимо использовать RxJS операторы catchError для отлова ошибок и switchMap для обработки внутренних Observable (грубо говоря "подписка в подписке").
Пример отлавливания нужной ошибки и обновления токена:
return next.handle(req).pipe(
catchError((error) => { // Отлавливаем ошибку
if (error instanceof HttpErrorResponse && error.status === 403) { // Проверяем тип необходимой ошибки
return refreshToken().pipe( // Вызываем некую функцию по обновлению токена
switchMap(() => {
const request = req.clone({
headers: req.headers.append('Authorization',
`Bearer ${localStorage.getItem('access_token')}`)
});
return next.handle(request); // Заново вызываем упавший запрос с обновленным токеном
})
);
}
return throwError(error); // Пробрасываем необработанные ошибки дальше
}));
В примере refreshToken() - это некая функция по обновлению/записи токена, которая возвращает Observable.