1
votes

I'm using Angular / Typescript. The application communicates with an API, an access_token is stored in storage.

On every request the interceptor checks if the access_token is still valid.

When the access_token is expired, the application needs to refresh the access_token with the refresh_token and continue with the last request (with the new access_token)

The problem I cannot get to clone the original request (with the expired token) and execute it. The call to request the new access_token is interfering with the interceptor. What is the best way to:

  1. Request the new token.
  2. Set the new token.
  3. Resume the call that had the expired token and clone to run successful?

The interceptor:

    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        // add authorization header with jwt token if available
        if (!this.isRefreshing) {
            if (this.authenticationService.checkLoginState()) {
                // Check if access_token is valid.
                this.currentUser = JSON.parse(localStorage.getItem('auth'));
                if (new Date(this.currentUser.access_token_expires) > new Date()) {
                    // Access token is valid, clone request and continue.
                    request = request.clone({
                        setHeaders: {
                            Authorization: `Bearer ${this.currentUser.access_token}`
                        }
                    });
                    return next.handle(request);
                } else {
                    // Token is not valid, request new token with reLogin().
                    this.reLogin().then(any => {
                        // New token has been set, now run old request with new token.
                        return next.handle(request);
                    }, error => {
                        this.authenticationService.logout();
                    });
                }
            }
        } else {
            return next.handle(request);
        }
    }

reLogin:

    reLogin() {
        return new Promise(function (resolve, reject) {
            this.isRefreshing = true;
            // Check if refresh token is valid
            if (new Date(this.currentUser.refresh_token_expires) > new Date()) {
                this.authenticationService.loginWithRefreshToken(this.currentUser.refresh_token).subscribe(data => {
                    // Using refresh_token was successful
                    this.currentUser = JSON.parse(localStorage.getItem('auth'));
                    this.isRefreshing = false;
                    resolve();
                }, error => {
                    console.error(error);
                    reject();
                });

        });
    }
1
make a if statement in interceptor to avoid a httpRequest which is having refreshtoken URL. I dont know whether it is a good way or not. but i am using the same - Akhil Naidu
I have an if statement in place ( if (!this.isRefreshing) ) this value is set to true when requesting the new token. - Wouter Doornbos
Im not sure whether it (isRefreshing) is maintaining the state or not try to check it from request.url.includes('refreshToken') (give url of API) - Akhil Naidu
I changed isRefreshing to the url, but still the same result. The next call is trying to run but giving me the error: TypeError: You provided 'undefined' where a stream was expected. You can provide an Observable, Promise, Array, or Iterable. - Wouter Doornbos

1 Answers

0
votes

A first remark would be to create a new const for the request when cloning it and not assigned it to the old one. The problem I think is when you are calling reLogin and expecting that in then you have access to a new request with the updated token. But you are still in the context of the old request. So you should probably return the access_token from the reLogin and set it on a clone of the request before proceeding with it.