Retry Http request with new headers in Angular 4.3 using Interceptor

Viewed 4182

How to handle cloned request with new headers?

I am trying to perform a cloned request in the new interceptors with the new token generated after the first request fail!

  • If err 401 -> refresh token
  • send back a copy of the previous request with the new headers

Here what I tried:

  refreshSessionToken() {
    return this.http.post('/connect', {})
      .map(res => {
        const token = res.json();
        localStorage.setItem('token', token);
      return token;
      });
  }


// Get the headers
const headers = getAuthenticationHeaders();
const reqMain = req.clone({headers: headers});
return next.handle(reqMain).catch(err => {

       this.refreshSessionToken()
            .subscribe(token => {
                const t = token.token_id;
                const clonedReq = req.clone({headers: req.headers.set('access-token', t)});

                return next.handle(clonedReq);
                    });
         })

getting a log of the clonedReq I can see that the token is refreshed, but the request (clonedReq) inside the subscribe is not performed, why?!

I tried other approaches on how to refresh the JWT but it doesn't seem to work in my case, any help on how to deal with it?

Thanks!

What do I expect to be my result?

  • Send 1# HTTP request
  • 1# request fail
  • Refresh token (get/set token)
  • Clone previous request and add refreshed token
  • Send 2# HTTP request
  • 2# request success

Similiar issues:

http-interceptor-refresh-jwt-token

3 Answers

Here is the solution which I got working with the latest version of Angular (7.0.0) and rxjs (6.3.3). Hope it helps.

export class SessionRecoveryInterceptor implements HttpInterceptor {
  constructor(
    private readonly store: StoreService,
    private readonly sessionService: AuthService
  ) {}

  private _refreshSubject: Subject<any> = new Subject<any>();

  private _ifTokenExpired() {
    this._refreshSubject.subscribe({
      complete: () => {
        this._refreshSubject = new Subject<any>();
      }
    });
    if (this._refreshSubject.observers.length === 1) {
      // Hit refresh-token API passing the refresh token stored into the request
      // to get new access token and refresh token pair
      this.sessionService.refreshToken().subscribe(this._refreshSubject);
    }
    return this._refreshSubject;
  }

  private _checkTokenExpiryErr(error: HttpErrorResponse): boolean {
    return (
      error.status &&
      error.status === 401 &&
      error.error &&
      error.error.message === "TokenExpired"
    );
  }

  intercept(
    req: HttpRequest<any>,
    next: HttpHandler
  ): Observable<HttpEvent<any>> {
    if (req.url.endsWith("/logout") || req.url.endsWith("/token-refresh")) {
      return next.handle(req);
    } else {
      return next.handle(req).pipe(
        catchError((error, caught) => {
          if (error instanceof HttpErrorResponse) {
            if (this._checkTokenExpiryErr(error)) {
              return this._ifTokenExpired().pipe(
                switchMap(() => {
                  return next.handle(this.updateHeader(req));
                })
              );
            } else {
              return throwError(error);
            }
          }
          return caught;
        })
      );
    }
  }

  updateHeader(req) {
    const authToken = this.store.getAccessToken();
    req = req.clone({
      headers: req.headers.set("Authorization", `Bearer ${authToken}`)
    });
    return req;
  }
}

I answered a similar question here. You can also have a read at my article here for the understanding of the code.

I answered a similar question here

You can't pass the cloned request into the next handler. Instead, use the HttpClient to re-try the cloned request.

this.http.request(clonedReq).subscribe(......);
Related