Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular JWT interceptor switch bearer token for refresh

Tags:

angular

jwt

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);
        })
      );
  }
like image 288
qlabfgerkaSmurf Avatar asked Dec 05 '25 10:12

qlabfgerkaSmurf


1 Answers

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.

like image 174
Tore Nestenius Avatar answered Dec 07 '25 01:12

Tore Nestenius