#angular #safari #jwt #interceptor
Вопрос:
У меня есть проект, в котором перехватчик настроен для обновления токена JWT, если http-запрос выполняется после истечения срока его действия, и пользователь решил остаться в системе (которая хранится, как и сами токены, в локальном хранилище).
Часть этого процесса обновления включает проверку, которая может изменить состояние входа пользователей (посредством субъекта отслеживания состояния в пользовательской службе), если обновление завершится неудачно или пользователь не решил остаться в системе.
Я подписался на это изменение состояния в главном компоненте приложения, и когда он обнаружит, что пользователь вышел из системы, он перейдет (с помощью router.navigateByUrl) пользователя на страницу входа.
Это правильно работает в любом браузере, кроме safari. И я действительно изо всех сил пытаюсь понять, почему это не удается. Сначала я подумал, что, возможно, это был молчаливый сбой, потому что я неправильно обрабатывал исключение обновления, но после явного обнаружения этой ошибки и выписки из журнала консоли, демонстрирующей вызов «navigateByUrl», фактически выполняется (путем размещения консоли.выписка из журнала в .затем обратный вызов вызова) Я совершенно не понимаю, почему браузер не обновляется.
// app.component catching the sign out to redirect user, note the 'Nav to the home page worked'
this.userService.getSignInStateSubject().subscribe(state => {
// if we were signed in and now we are not, redirect user immediately
console.log('why has this not changed', state, this.signedInState);
if (!state amp;amp; this.signedInState) {
// adding cleanup for log out here, rather than scatter it all over the place
this.productService.setCurrentSelectedProduct(undefined);
console.log('this needs to change immediately');
this.routerService.navigateByUrl('/login').then(success => {
console.log('Nav to the home page worked');
}, error => {
console.log(error);
});
}
this.signedInState = state;
});
// auth interceptor
return next.handle(authReq).pipe(catchError(err => {
// we need to make sure we dont try to refresh on authentication failure
if ([400, 401].indexOf(err.status) > -1 amp;amp; req.url !== environment.api_url '/' environment.tokens) {
userService.redirectUrl = routingService.url;
// convert to an observable and pipe the result
return from(userService.attemptTokenRefresh()).pipe(
catchError(error => {
console.log('this caught the error and did nothing with it');
return of(error);
}),
switchMap(result => {
console.log('result', result);
if (result instanceof String) {
console.log('this got a real result');
authReq = req.clone({
setHeaders: { Authorization: 'Bearer ' result }
});
return next.handle(authReq);
} else {
console.log('this has not worked well at all', result);
return EMPTY;
}
}),
);
} else {
return throwError(err);
}
}));
\ user service 'refresh' method
public attemptTokenRefresh(): Promise<string> {
return new Promise((resolve, reject) => {
// console.log('here we are doing the refresh', localStorage.getItem(this.STAY_SIGNED_IN_KEY), Boolean(Number(localStorage.getItem(this.STAY_SIGNED_IN_KEY))), localStorage.getItem(this.REFRESH_TOKEN_KEY));
// reject if user chose not to stay signed in
if (!Boolean(Number(localStorage.getItem(this.STAY_SIGNED_IN_KEY)))) {
this.clearStoredData();
this.userSubject.next(undefined);
reject('Refresh failed as user chose not to stay signed in');
return;
}
const refreshToken = localStorage.getItem(this.REFRESH_TOKEN_KEY);
// reject if we have a missing or invalid refresh token
if (!refreshToken) {
this.clearStoredData();
this.userSubject.next(undefined);
reject('Refresh failed because of invalid refresh token');
return;
}
// attempt a refresh
this.httpClient.post(environment.api_url '/' environment.refresh_tokens, JSON.stringify({ refreshToken }), {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
}).subscribe((authResult: { accessToken: string, refreshToken: string }) => {
// update the stored tokens
localStorage.setItem(this.ACCESS_TOKEN_KEY, authResult.accessToken);
localStorage.setItem(this.REFRESH_TOKEN_KEY, authResult.refreshToken);
const newTokenObject = this.parseAccessToken(authResult.accessToken);
// resolve with the updated token for use ether in the init function or auth interceptor
resolve(authResult.accessToken);
}, error => {
this.clearStoredData();
this.userSubject.next(undefined);
reject(error);
});
});
}
Кто-нибудь видит причину, по которой это не должно сработать? (И, в частности, не должен работать в Safari против chrome, где он, похоже, работает должным образом)