4

I am following this example: https://www.intertech.com/Blog/angular-4-tutorial-handling-refresh-token-with-new-httpinterceptor/

All works well, except the method handle401Error. It does not execute, unless I add subscribe in the interceptor. I must be missing something, even though trying to follow that example.

Here's the relevant code:

Component:

ngOnInit() {
  this.service.getData()
    .subscribe((response: any) => {
      this.message = `Worked with status = ${response.status}`;
    },
    error => this.message = `Failed with status = ${error.status}`);
}

Service:

constructor(private http: HttpClient) {
  this.currentToken = this.authTokenStale;
}

public authTokenStale: string = 'stale_auth_token';
public authTokenNew: string = 'new_auth_token';
public currentToken: string;

getData() {
  return this.http.get<{status}>('https://private-4002d-testerrorresponses.apiary-mock.com/getDataError401');
}

getAuthToken() {
  return this.currentToken;
}

refreshToken(): Observable<string> {
  this.currentToken = this.authTokenNew;
  return Observable.of(this.authTokenNew).delay(200);
}

Interceptor:

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

intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
  console.log('interceptor fired!')
  let service = this.inj.get(Service);
  // addToken() returns headers, getAuthToken() returns token
  return next.handle(this.addToken(request, service.getAuthToken())).do((event: HttpEvent<any>) => {
    if (event instanceof HttpResponse) {
      // success
    }
  }, (err: any) => {
    if (err instanceof HttpErrorResponse) {
      if (err.status === 401) {
        return this.handle401Error(request, next);
      }
    }
  });
}

handle401Error:

handle401Error(req: HttpRequest<any>, next: HttpHandler) {
  if (!this.isRefreshingToken) {
    this.isRefreshingToken = true;
    let service = this.inj.get(Service);
    this.tokenSubject.next(null);
    return service.refreshToken()
        .switchMap((newToken: string) => {
          console.log('goes to switchMap') // does not come here!
            if (newToken) {
                this.tokenSubject.next(newToken);
                return next.handle(this.addToken(this.getNewRequest(req), newToken));
            }
            return this.logoutUser();
        })
        .catch(....)
        //.subscribe(() => {) // only works!
        .finally(() => {
          console.log('goes to finally')
            this.isRefreshingToken = false;
        });
  } else {
    return this.tokenSubject
        .filter(token => token != null)
        .take(1)
        .switchMap(token => {    
            return next.handle(this.addToken(this.getNewRequest(req), token));        
        });
  }
}

Sorry, a bit of code, but I think all is relevant to be able to reproduce the issue.

I also made a

StackBlitz

to showcase the issue!

Community
  • 1
  • 1
DRNR
  • 433
  • 2
  • 8
  • 21
  • see my answer in https://stackoverflow.com/questions/47417899/angular-4-and-oauth-intercept-401-responses-refresh-the-access-token-and-retr/47420967?noredirect=1#comment81798323_47420967 – Eliseo Nov 22 '17 at 07:43
  • @Eliseo Thank you, seemed to work fine! :) – DRNR Nov 24 '17 at 07:16

2 Answers2

9

If you use Angular 6, remember about changes in RxJS library. In new RxJS, line with do operator

next.handle(req).do(() => {});

must be changed to:

next.handle(req).pipe(tap(() => {}));

Check this article for more info:

  1. https://www.academind.com/learn/javascript/rxjs-6-what-changed/
  2. https://auth0.com/blog/whats-new-in-rxjs-6/
witek1902
  • 197
  • 1
  • 3
  • 12
3

I solved this by moving the logic from error to a catch instead from https://stackoverflow.com/a/47420967/7741865

return next.handle(authReq).catch((err: any) => { //<--if error use a catch
  if (err instanceof HttpErrorResponse) {
    if (err.status === 401) {
      //use switchMap to really return next.handle(authReq)
      return auth.refreshToken().switchMap((value: any) => {
        let httpHeaders = new HttpHeaders()
          .set('Content-Type', 'application/json; charset=utf-8')
          .set('Authorization', '' + auth.getAuthToken())
        const authReq = req.clone({ headers: httpHeaders });
        return next.handle(this.getNewRequest(req));
      })
    };
  }
  //Other case throw an error
  return Observable.throw(err);
});

StackBlitz

DRNR
  • 433
  • 2
  • 8
  • 21