Search code examples
nestjsnestjs-interceptor

How to add a NestJS interceptor for outgoing requests?


I am creating a NestJS application that acts as middleware between this API and something else. I need it to send every request with a Bearer token. If the token is rejected I want it to fetch a new token and retry the same request again.

I'm using an interceptor, and the basics here seem simple enough where I simply add the token to the request headers. However, the other service is always returning a "403 forbidden" error. When I inspect further it seems that the Authorization header was not actually added to the request!

Here is asimplified version

intercept(context: ExecutionContext, next: CallHandler) {
  const request = context.switchToHttp().getRequest<Request>();
  request.headers.Authorization = this.token;
  return next.handle();
}

Is this not the correct way to add an Authorization header?

Here's my full interceptor code

@Injectable()
export class AuthTokenInterceptor implements NestInterceptor {
  private readonly authUrl = "https://...";
  private token = "";

  constructor(private readonly http: HttpService) {
    //Get the token as soon as possible
    this.trySaveToken();
  }

  intercept(context: ExecutionContext, next: CallHandler) {
    const request = context.switchToHttp().getRequest<Request>();

    if (!request.headers.Authorization) {
      request.headers.Authorization = this.token;
    }

    return next.handle().pipe(
      catchError((error: AxiosError) => {
        const originalRequest = error.config;

        console.log(originalRequest?.headers); //this has no `Authorization` header on it!

        //If we have an auth error, we probably just need to re-fetch the token
        if (error.response?.status === 401 && originalRequest != null && originalRequest.url !== this.authUrl) {
          //without second check, we can loop forever
          this.trySaveToken();
          originalRequest.headers.Authorization = this.token; //override old default for this request
          return of(originalRequest); //retry request with newly added
        } else {
          return throwError(() => error);
        }
      }),
  }

  private trySaveToken() {
    this.http.post(this.authUrl).subscribe({
      next: (response) => {
        this.token = 'Bearer ' + response.data.access_token;
      },
    });
  }
}

My retry logic might not be correct yet, but I can't really work on that until I get the first authorization issue solved first

here is what the console.log produces

Object [AxiosHeaders] {
  Accept: 'application/json, text/plain, */*',    
  'Content-Type': 'application/json',
  'User-Agent': 'axios/1.6.8',
  'Content-Length': '162',
  'Accept-Encoding': 'gzip, compress, deflate, br'
}

Solution

  • It turns out that I need to be setting the Authorization header like this

    this.http.axiosRef.defaults.headers.common.Authorization = this.token;
    

    I do not fully understand why I have to do this, but it does work