angularrxjsinterceptor

Using interceptor to attach the correct token after server responds with an 401 error


I am trying to create an error-handling method for all authentication issues. I am running into an issue where my authTokenService.getAmadeusAuthToken$ method is being called but since it's a post-call that returns a new token, it takes longer to resolve, in the meantime the catchError obvservable returns the next.handle before the token is retrieved. I have tried a few different ways and I don't know the best way to have interceptor method wait until the authTokenService.getAmadeusAuthToken$ is completed. Here is my code:

My Interceptor:

intercept(req: HttpRequest<any>, next: HttpHandler) {
  return next.handle(req).pipe(
    catchError((response: HttpErrorResponse) => {
      return next.handle(req).pipe(
        tap(test => {
          this.authTokenService.getAmadeusAuthToken$().pipe(
          ).subscribe(token => {
            const authReq = req.clone({
              headers: req.headers.append('Authorization', 'Bearer ' + token.access_token)
            });
            return next.handle(authReq);
          })
        })
      )
    })
  );
}

The method to post call to grab a new token

getAmadeusAuthToken$(): Observable<any> {
  const httpOptions = { headers: new HttpHeaders().set('Content-Type', "application/x-www-form-urlencoded") };
  return this.http.post<Token>('https://test.api.amadeus.com/v1/security/oauth2/token', this.body, httpOptions);
}

To make sure the intercept method works, I made an API call to just grab the new token and hardcoded it in. The intercept method work like a charm since there isn't a race condition and the interceptor returns the next.handle() with the token right away.

intercept(req: HttpRequest<any>, next: HttpHandler) {
  return next.handle(req).pipe(
    catchError((response: HttpErrorResponse) => {
      const authReq = req.clone({
        headers: req.headers.append('Authorization', 'Bearer ' + "lkVkfilWizfcuyVpXBQsA08XkUy5")
      });
      return next.handle(authReq);
    })
  );
}

So basically I want to make sure the interceptor waits for the authTokenService.getAmadeusAuthToken$ to finish getting the token, and only then return the new next.handle with the token, what am I missing here?


Solution

  • This eliminates the race condition I had and makes sure the request waits for the new token response before trying the request again. This is a quick and dirty way of doing it, Further refinement will need to be done in order to cache the token in local storage and put in a filter for specific error codes

     export class AuthInterceptor implements HttpInterceptor {
    
      token :  Token;
      idTokenPresent = false;
      authRequest: any;
      idToken: string | string[] | null;
      testreq: any;
      constructor(private authTokenService: AuthTokenService) {}
    
      intercept(req: HttpRequest<any>, next: HttpHandler) {
    
        return next.handle(req).pipe(
          catchError((response: HttpErrorResponse) => {
            return this.authTokenService.getAmadeusAuthToken$().pipe(
              switchMap(token => {
                this.authRequest = req.clone({
                headers: req.headers.append('Authorization', 'Bearer ' + token.access_token)
                 });
                return next.handle(this.authRequest)
              })
            )
          }
        )
        )
      }
    }