Issue
I have a jwt/refresh token authorization.
When /api/auth/token/refresh
fails with a BadRequest due to refresh token not existance/expiration, I'm trying to catch the error through catchError
and instead of throwing the error in the console log, I'm doing next.handle(request)
to suppress the error. Currently, whenever the catchError comes, it runs in an infinite loop. In other words, after I catch the error, I want to logout()
user, which executes an HTTP post request itself and then navigate to the login page.
The reason I'm doing return this.authService.logout()
is because I have an HTTP post request in logout()
and if I don't subscribe somehow, it won't execute it.
auth.interceptor.ts
import { Injectable } from '@angular/core';
import {
HttpEvent,
HttpInterceptor,
HttpHandler,
HttpRequest,
HttpErrorResponse
} from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError, switchMap } from 'rxjs/operators';
import { Router } from '@angular/router';
import { AuthService } from '../services/auth.service';
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
constructor(
private router: Router,
private authService: AuthService) { }
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(request)
.pipe(
catchError((error: HttpErrorResponse) => {
if (error instanceof HttpErrorResponse) {
if (error.status === 401) {
return this.handleHttpResponseError(request, next);
}
}
return throwError(error);
})
);
}
private handleHttpResponseError(request: HttpRequest<any>, next: HttpHandler) {
const accessToken = this.authService.getJwtToken();
// if jwt token is not set, we just let the request execute
if (!accessToken) {
return next.handle(request);
}
// if jwt token has not expired yet, we add the authorize header
// otherwise we refresh the token
if (!this.authService.isTokenExpired()) {
// add "Authorization: Bearer token" to the current request's headers
return next.handle(this.attachTokenToRequest(request, accessToken));
} else {
// when the JWT token expires
return this.authService.refreshToken()
.pipe(
switchMap(token => {
console.log('Token refreshed.');
return next.handle(this.attachTokenToRequest(request, token));
}),
catchError(error => {
console.log('INFINITE LOOP HERE.');
return this.authService.logout()
.pipe(
switchMap(error2 => {
console.log(error2);
this.router.navigate['/log-in'];
return next.handle(request);
})
);
})
);
}
}
private attachTokenToRequest(request: HttpRequest<any>, token: string) {
return request.clone({
setHeaders: {
Authorization: `Bearer ${token}`
}
});
}
}
// auth.service.ts
logout() {
return this.httpClient.post<AccessToken>(`${this.actionUrl}/token/revoke`, { 'refreshToken': this.getRefreshToken() })
.pipe(
map(() => {
console.log('revoked token');
localStorage.removeItem(this.JWT_TOKEN);
localStorage.removeItem(this.REFRESH_TOKEN);
return true;
})
);
}
Solution
I faced to this problem and finally figure it out that it was because of a routing rule!
The problem was happening because i set onSameUrlNavigation: 'reload'
in app-routing.module.ts so once i redirected page to login page from HttpErrorInterceptor class, the Interceptor was falling in an infinite loop.
So we have two choices to prevent happening this infinite loop:
Removing
onSameUrlNavigation: 'reload'
Or
Prevent happening multiple redirects with checking if it already is login page: So in the http-error.interceptor.ts file you can import
Router
and use a condition:if(this.router.url != '/login') this.router.navigate(["login"]);
It works for me and hope it could help others.
Answered By - Akram Rabie
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.