0
votes

I'm trying to write HttpInterceptor that is refreshing the API access token when it's near it's expiration.

The main problem is to hold any HTTP request before the refresh token request comes back. If the token was refreshed successfully, the given request can fire and if not, the application should do the auto logout.

In short:

  • I have 2 Observables
  • I want to subscribe to the first one (refresh token)
  • if this succeeds, I want to return the second observable (request from method params)
  • if not, I want to auto-logout and throw an Observable

Here is my current code:

@Injectable()
export class RefreshTokenInterceptor implements HttpInterceptor {

  constructor(private authenticationService: AuthenticationService) { }

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

    if (this.isRefreshOfAccessTokenUnnecessary()) {
      return next.handle(request);
    }

    return this.authenticationService.refreshToken()
      .concatMap(() => next.handle(request))
      .filter((item) => item instanceof HttpResponse)
      .catch(() => this.refreshTokenFail());
  }


  private isRefreshOfAccessTokenUnnecessary(): boolean {
    ...
  }

  private refreshTokenFail(): ErrorObservable {
    this.authenticationService.logOut();
    return Observable.throw(new Error('not authorized'));
  }
}

The thing is that I have troubles with using the appropriate Observable operator for that. concatMap seems to work, but it's not perfect. The token refresh happens without problems, the other requests are waiting, but this results in duplicated requests sometimes.

Can someone point me which operator from RxJS would be appropriate here? The documentation is not very helpful to be honest.

I'm not using lettable operators from RxJS yet.

1
Hey sir, take a look here Refresh Token in angular 7+ - Matheus Ferreira

1 Answers

0
votes

You can use async and await as shown in this plunkr

For example, you'll have an async method that will return a promise with desired result (it will be the first call you want to wait before doing anything else).

Then you can call it in an other async method and await it. (you can await it in a try/catch if you want).

Be careful: await only works with promises, but you can resolve an observable in the async method that returns the promise to await.