5

I have built my AuthInterceptor which on a 401 error sends a request to get a new token.

The handle401Error method is called when I encounter a 401 error, But I am trying to wait other HTTP requests until I got the new token. But it's not waiting until to get the new refresh token although it is making an HTTP call to and getting a new access token. Please find the screenshot I have attached.

enter image description here

Interceptor.ts

isRefreshingToken = false;
tokenSubject: BehaviorSubject<string | null> = new BehaviorSubject<string | null>(null);

intercept(
    request: HttpRequest<any>,
    next: HttpHandler
): Observable<HttpEvent<any>> {

    const timeOut = appSettings.ajaxTimeout;
    const retryCount = appSettings.retryCount;
    const retryDelay = appSettings.retryDelayMS;

    return next.handle(request).pipe(
        timeout(timeOut),
        catchError((error) => {
            if (error instanceof HttpErrorResponse) {
                const httpErrorCode: number = error['status'];
                switch (httpErrorCode) {
                    case StatusCodes.BAD_REQUEST:
                        return throwError(error);
                        //return this.handle400Error(error);
                    case StatusCodes.UNAUTHORIZED:
                        return this.handle401Error(request, next);
                    default:
                        this._toastr.error(
                            'Sorry! something went wrong.',
                            'Error!'
                        );
                        return throwError(error);
                }
            } else {
                return throwError(error);
            }
            }),
            retryWhen((errors) => {
                return errors.pipe(
                    concatMap((error, count) => {
                        if (count < retryCount) {
                            return of(error);
                        }
                        return throwError(error);
                    }),
                    delay(retryDelay)
                );
            })
        );
    }

    handle401Error(
        request: HttpRequest<any>,
        next: HttpHandler
    ): Observable<HttpEvent<any>> {
        if (!this.isRefreshingToken) {
            this.isRefreshingToken = true;
            console.log('401');

            // Reset here so that the following requests wait until the token
            // comes back from the refreshToken call.
            this.tokenSubject.next(null);

            return this._spotify.getRefreshToken().pipe(
                switchMap((authData: ISpotifyTokens) => {
                    if (authData) {
                        console.log('new token success');
                        this.updateTokenInCookie(authData);
                        this.tokenSubject.next(authData.access_token);
                        return next.handle(request);
                    }
                    return this.logoutUser();
                }),
                catchError((error) => {
                    // If there is an exception calling 'refreshToken', bad news so logout.
                    return this.logoutUser();
                }),
                finalize(() => {
                    this.isRefreshingToken = false;
                })
            );
        } else {
            return next.handle(request);
        }
    }

    updateTokenInCookie(authData: ISpotifyTokens) {
        this._spotify.updateTokensInStorage(authData);
        this._spotify.startRefreshTokenTimer(authData.expires_in);
    }

    logoutUser() {
        this._spotify.clearTokensFromStorage();
        this._router.navigate(['/welcome']);
        return throwError('');
    }
2
  • Does this help stackoverflow.com/questions/57637923/… Commented Jun 29, 2021 at 5:35
  • @dev, my implementation is mostly same, is there any error? Commented Jun 29, 2021 at 5:39

1 Answer 1

2

I have solved the problem using the code below. If anyone has any better solution please suggest it.

private isRefreshingToken = false;
private timeOut = appSettings.ajaxTimeout;
private tokenSubject: BehaviorSubject<any> = new BehaviorSubject<any>(null);

intercept(
    request: HttpRequest<any>,
    next: HttpHandler
): Observable<HttpEvent<any>> {
    return next.handle(request).pipe(
    timeout(this.timeOut),
    catchError((error) => {
        if (error instanceof HttpErrorResponse) {
        const httpErrorCode: number = error['status'];
        switch (httpErrorCode) {
            case StatusCodes.BAD_REQUEST:
              return throwError(error);
            case StatusCodes.UNAUTHORIZED:
              return this.handle401Error(request, next);
            default:
              this._toastr.error(
                'Sorry! something went wrong.',
                'Error!'
              );
              return throwError(error);
           }
           } else {
             return throwError(error);
           }
       })
    );
}

private handle401Error(
    request: HttpRequest<any>,
    next: HttpHandler
): Observable<HttpEvent<any>> {
        if (!this.isRefreshingToken) {
            this.isRefreshingToken = true;
            // Reset here so that the following requests wait until the token
            // comes back from the refreshToken call.
            this.tokenSubject.next(null);
            return this._spotify.getRefreshToken().pipe(
                switchMap((response: ISpotifyTokens) => {
                    console.log('updating on 401');
                    // Updating new token in cookie
                    this._spotify.updateTokensInStorage(response, false);
                    this.tokenSubject.next(response.access_token);
                    return next.handle(
                        this.addTokenInHeader(request, response.access_token)
                    );
                }),
                catchError((error) => {
                    // If there is an exception calling 'refreshToken', bad news so logout.
                    this.logoutUser();
                    return throwError('');
                }),
                finalize(() => {
                    this.isRefreshingToken = false;
                })
            );
        } else {
            return this.tokenSubject.pipe(
                filter((token) => token != null),
                take(1),
                switchMap((token) => {
                    return next.handle(this.addTokenInHeader(request, token));
                })
            );
        }
    }

addTokenInHeader(
    request: HttpRequest<any>,
    token: string
): HttpRequest<any> {
    return request.clone({
        setHeaders: { Authorization: 'Bearer ' + token }
    });
}

logoutUser(): void {
    this._spotify.clearTokensFromStorage();
    this._router.navigate(['/welcome']);
}
Sign up to request clarification or add additional context in comments.

Comments

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.