0
votes

I have the 401 interceptor, when the access_token is expired, with a successfully case with one request. The interceptor reloads the token and it returns the next.handle(customReq). But I have a problem when 2 or more request at the same time and both request has expired tokens, because the second request try to refresh again, but now the refresh token is invalid. So.... I tried to set a flag to do it only once, and using a custom observable to return. The problem is the component never now if it was successfully or not, and I can't remove the loader.

the http interceptor:

    return next
        .handle(customReq).pipe(
        tap((ev: HttpEvent < any > ) => {
            if (ev instanceof HttpResponse) {
                this.setApiStatus(ApiStatusCode.ACTIVE);
                this.interceptorIgnore.removeIgnore(request.url);
            }
        }),
        catchError(resp => {
            if (resp instanceof HttpErrorResponse) {
                switch (true) {
                    // Unauthorized
                    case resp.status === 401:
                        return this.attemptToRefreshToken(apiRequest, customReq, next);
                    // More code unnecessary ....
                }
            }

            return throwError(resp);
        })
    );

attemptToRefreshToken method:

attemptToRefreshToken(apiRequest: ApiRequest, customReq: HttpRequest<any>, next: HttpHandler) {


        const retryRequest = () => {
            this.updatingToken = false;
            customReq = customReq.clone({
                headers: apiRequest.buildHeader()
            });
            return next.handle(customReq);
        };

        if (!this.updatingToken) {
            this.updatingToken = true;
            const tokenRequestData = this.apiAuth.refreshTokenData();
            return this.http.request(tokenRequestData.opts.method, tokenRequestData.url, tokenRequestData.opts)
            .pipe(flatMap((token: ResponseTokenFormat) => {
                ApiAuth.storedToken = token;
                this.usersService.queryUsersUsename(ApiAuth.storedUser).then(([user]) => {
                    if (user && !user.isAdmin) {
                        this.sessionService.setUserPermissions(user);
                    }
                });
                this.tokenUpdated.next();
                return retryRequest();
            }));
        }

        return this.tokenUpdated$.pipe(
            flatMap(() => retryRequest())
        );       

}

The retryRequest executes when the next() is invoked and works fine, but it never inform to service witch call this http request. The return retryRequest(); when I refresh the token works perfectly and the service can subscribe.

What I am doing wrong? Maybe the Observable?

1

1 Answers

0
votes

I think you may have to use exhaustMap instead of flatMap, in attemptToRefreshToken.

How they work?

  • flatMap: creates an Observable immediately for any source item, all previous Observables are kept alive
  • exhaustMap: the source items are ignored while the previous Observable is not completed.