1

I am trying to implement token interceptor in my Angular front end, but I have one issue with my current implementation.

My interceptor look like this:

  intercept(request: HttpRequest<any>,next: HttpHandler): Observable<HttpEvent<any>> {
    if (request.headers.get("No-Auth") === "True") {
      return next.handle(request);
    }

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

And handle404Error function:

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

      return this.authService.refreshToken().subscribe(
        (res: any) => {
          if (Boolean(res) === true) {
            this.isRefreshing = false;
            this.refreshTokenSubject.next(res);
            return next.handle(request);
          } else {
            return this.authService.logout();
          }
        },
        err => {
          return this.authService.logout();
        },
        () => {
          this.isRefreshing = false;
        }
      );
    } else {
      return this.refreshTokenSubject.pipe(
        filter(res => res != null),
        take(1),
        switchMap(() => {
          return next.handle(request);
        })
      );
    }
  }

When I make and API call and server return 404, my interceptor will make a call to /token endpoint to obtain new JWT.

Note that I am storing JWT in session object on server (in memory) and on client I have only sessionID and refresh token as HttpOnly cookies.

The problem is, when I make an API call and JWT is not valid, it will set new JWT, but it doesnt send previous request again. (I have to press button twice to get data after JWT expires).

Do you have any idea, how to implement this logic?

Thanks for you help.

1 Answer 1

1

First thing which is not right - you subscribe inside interceptor, so you get nested subscribe, because Angular subscribes to your request internally.

What you actually want is to return your return next.handle(request); from catchError. Example:

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

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

    return this.authService.refreshToken().pipe(
      mergeMap((res: any) => {
        if (Boolean(res) === true) {
          this.isRefreshing = false;
          this.refreshTokenSubject.next(res);
          return next.handle(request);
        } else {
          /* return */ this.authService.logout();
          return of(undefined) // added this because I dont know what this.authService.logout() returns. It must be an Observable
        }
      }),
      catchError((err) => {
        /* return */ this.authService.logout();
        return throwError(err);
      }))      
  } else {
    return this.refreshTokenSubject.pipe(
      filter(res => res != null),
      take(1),
      switchMap(() => {
        return next.handle(request);
      })
    );
  }
}

Note that I havent checked for any typos. Also I discarded this block:

() => {
  this.isRefreshing = false;
}

If you want to execute it when this.authService.refreshToken() completes, you can add finalize operator next to the mergeMap and catchError.

Explanation what actually happens here is simple - you return one Observable from intercept method. Just pipe all the API calls together and returned stream will execute them.

Sign up to request clarification or add additional context in comments.

2 Comments

It is working! But with one small problem - When I login and make an call to protected route, it will return an result, but after token expire and I click again, call to /token wont start, but after I refresh the page and click the button, it work as I expect.
Hm.Maybe it passes this statement - request.headers.get("No-Auth") === "True". Cant tell much here. Try to put some console.logs and see when it fails to execute something.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.