Search code examples
angularrestjwtrefresh-token

Angular JWT retry REST call after refresh token with multiple interceptors


I'm using Angular 13.

Currently my error.interceptor it automatically catches the 401 error and automatically refreshes the jwt token.

In addition to this, I would need the error.interceptor try to make the previously intercepted call just after the subscribed "validateNewToken()".

Below is an example of my error.interceptor and the auth.interceptor:

error.interceptor

@Injectable()
export class ErrorInterceptor implements HttpInterceptor {
  constructor(private authService: AuthService, private router: Router, private toastr: ToastrService) {
  }
  intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
    return next.handle(request)
      .pipe(catchError((err: HttpErrorResponse) => {
        console.error('Error Intercepted', err)
        if(err.error.code == '40102' || err.error.error == 'unauthorized_client'){
          this.authService.refreshToken().subscribe((res ) => {
            localStorage.setItem('access_token', res.access_token);
            this.authService.validateNewToken(res.access_token).subscribe((resVer: any) => {
              localStorage.setItem('username', resVer.sub);
            })
          })
        }
        return throwError(() => err);
      }));
  }
}

auth.interceptor

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
  constructor(private authService: AuthService) { }
  intercept(req: HttpRequest<any>, next: HttpHandler) {
    const authToken = this.authService.getToken();
    const username = localStorage.getItem('username');
    if (authToken && username) {
      req = req.clone({
        setHeaders: {
          'Authorization': `Bearer ${authToken}`,
          'OAM_REMOTE_USER': username,
          'rejectUnauthorized' : 'false'
        }
      })
    } 
    return next.handle(req)
  }
}

Thanks in advance


Solution

  • MY SOLUTION EDIT error.interceptor:

    @Injectable()
    export class ErrorInterceptor implements HttpInterceptor {
    
      constructor(private authService: AuthService, private router: Router, private toastr: ToastrService) {
      }
    
    
      private isRefreshing = false;
      private refreshTokenSubject: BehaviorSubject<any> = new BehaviorSubject<any>(null);
    
      intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<Object>> {
        let authReq = req;
        const token = this.authService.getToken();
        const authToken = this.authService.getToken();
        const username = localStorage.getItem('username');
        if (token != null) {
          authReq = this.addTokenHeader(req);
        }
        return next.handle(authReq).pipe(catchError(error => {
          if (error instanceof HttpErrorResponse && (error.error.code == '40102' || error.error.error == 'unauthorized_client')) {
            return this.handle401Error(authReq, next);
          }
          return throwError(() => error);
        }));
      }
    
    
      private handle401Error(request: HttpRequest<any>, next: HttpHandler) {
        if (!this.isRefreshing) {
          this.isRefreshing = true;
          this.refreshTokenSubject.next(null);
    
          return this.authService.refreshToken().pipe(
            switchMap((res: any) => {
              localStorage.setItem('access_token', res.access_token);
              this.authService.verifyToken(res.access_token).subscribe((resVer: any) => {
                localStorage.setItem('username', resVer.sub);
                this.isRefreshing = false;
                this.refreshTokenSubject.next(res.access_token);
              })
              return next.handle(this.addTokenHeader(request));
            }),
            catchError((err) => {
              this.isRefreshing = false;
              return throwError(() => err);
            })
          );
        }
        return this.refreshTokenSubject.pipe(
          filter(token => token !== null),
          take(1),
          switchMap((token) => next.handle(this.addTokenHeader(request)))
        );
      }
      private addTokenHeader(request: HttpRequest<any>) {
        const authToken = this.authService.getToken();
        const username = localStorage.getItem('username');
          
        if (authToken && username) {
          return request = request.clone({
            setHeaders: {
              'Authorization': `Bearer ${authToken}`,
              'OAM_REMOTE_USER': username,
              'rejectUnauthorized': 'false'
            }
          })
        } else {
          return request;
        }
      }
    }