Search code examples
angularrestoauth-2.0

ngx-restangular refresh token once for multiple expired access tokens


I'm using Angular 5 and ngx-restangular. I want to send a refresh token whenever the token is expired. I used the code provided by the library. It works fine when just one request is sent! The request gets an error and then it sends a refresh token request and then resends the request with the new access token.

But when I have two requests at the same time, both of them get error and both send a refresh token request. The first one gets the new access token but the second one gets an error as it's sending the old refresh token.

What is the solution? is it server or client side?


Solution

  • My solution for this problem was creating an array of the requests that are pending for the refreshedToken. So when I get a 401 error (access token expired), I request for a new token and also make a flag = true. While this flag is true, I add the requests to the array. When the new token was resolved, I replace the token to the requests in the array, and then repeat them.

    Here is my code:

    app.module.ts

    RestangularProvider.addErrorInterceptor((response, subject, responseHandler) => {
      if (response.status === 401) {
        if (AuthService.refreshingToken) {
          // refreshTokenRequest is already sent and we should wait. So we add the request to the pending array
          AuthService.requestsPendingRefreshToken.push({ response, subject, responseHandler });
        } else {
          refreshAccesstoken()
            .pipe(
              switchMap(refreshAccesstokenResponse => {
                const newHeaders = new HttpHeaders({ Authorization: 'Bearer ' + refreshAccesstokenResponse });
                const newReq = response.request.clone({ headers: newHeaders });
                return response.repeatRequest(newReq);
              })
            )
            .subscribe(res => responseHandler(res), err => subject.error(err));
        }
        return false; // error handled
      }
      return true; // error not handled
    });
    

    auth.service.ts

    refreshAccesstoken() {
      return this._http.get(url, requestOptions).pipe(
        map(accessToken => {
          this.repeatPendingRequests();
          this.refreshingToken = false;
          return accessToken;
        })
      );
    }
    repeatPendingRequests() {
      this.requestsPendingRefreshToken.forEach(pr => {
        const { response, subject, responseHandler } = pr;
        const newHeaders = new HttpHeaders({ Authorization: 'Bearer ' + this.accessToken });
        const newReq = response.request.clone({ headers: newHeaders });
        response.repeatRequest(newReq).subscribe(res => responseHandler(res), err => subject.error(err));
      });
      this.requestsPendingRefreshToken = [];
    }