Home > Software design >  Angular JWT interceptor switch bearer token for refresh
Angular JWT interceptor switch bearer token for refresh

Time:12-09

I have implemented a JWT and refresh token flow. When I implemented this in the past, I did it a bit differently, mainly that the refresh token was sent in the body.

But now I've done it differently, and I have to send the access token via the Authorization header, but my interceptor code doesn't want to switch out the bearer token. How do I fix, that if I'm wanting to refresh, I actually use the refresh token as the bearer token instead of the access token that has expired?

intercept(
    request: HttpRequest<any>,
    next: HttpHandler
  ): Observable<HttpEvent<any>> {
    const token = this.userService.getJWTToken();
    if (token) {
      request = this.addToken(request, token);
    }

    return next.handle(request).pipe(
      catchError((error) => {
        if (error instanceof HttpErrorResponse && error.status === 401) {
          return this.handle401Error(request, next);
        } else if (error.status !== 0) {
          return throwError(error);
        }
      })
    );
  }

private addToken(request: HttpRequest<any>, token: string): HttpRequest<any> {
    return request.clone({
      setHeaders: {
        Authorization: `Bearer ${token}`,
      },
    });
  }

  private handle401Error(
    request: HttpRequest<any>,
    next: HttpHandler
  ): Observable<HttpEvent<any>> {
    if (!this.isRefreshing) {
      this.isRefreshing = true;
      this.refreshTokenSubject.next(null);

      //This is what I've tried, to switch out the tokens
      request = this.addToken(request, this.userService.getRefreshToken());

      //this.userService.refreshToken() is a POST request, where I want the refresh token as the bearer token, instead of the access token
      return this.userService.refreshToken().pipe(
        switchMap((token: TokenDTO) => {
          this.isRefreshing = false;
          this.refreshTokenSubject.next(token.accessToken);
          return next.handle(this.addToken(request, token.accessToken));
        })
      );
    } else {
      return this.refreshTokenSubject.pipe(
        filter((token) => token != null),
        take(1),
        switchMap((accessToken) =>
          next.handle(this.addToken(request, accessToken))
        )
      );
    }
  }

I have also tried setting the HTTP header to the authorization bearer token, in the post request

public refreshToken(): Observable<TokenDTO> {
    const headers = new HttpHeaders({
      'Content-Type': 'application/json',
      Authorization: `Bearer ${this.getRefreshToken()}`,
    });

    return this.httpClient
      .post<TokenDTO>(`${this.hostname}/users/refreshToken`, {}, headers)
      .pipe(
        tap((tokens: TokenDTO) => {
          this.saveTokens(tokens);
        })
      );
  }

CodePudding user response:

You should not replace the access token with the bearer token to refresh it, instead you call the dedicated token endpoint with your refresh token to get a new access token. Sometimes, depending on the setup, you might get back a new refresh token as well.

CodePudding user response:

You shouldn't use the refresh token in the Authorization header. This means that you should not authorize the request based only on the value of the refresh token. If you do that then anyone who steals the refresh token will be able to use it to get a new access token! The refresh token is the data needed to issue new tokens, but you should authorize the client (your app) in another way. E.g. you can use HTTP Basic authentication and pass the client's ID and secret, or use more sophisticated stuff, like mutual TLS.

  • Related