0

Im trying to implement this angular refresh token interceptor (also from this stackoverflow) but having trouble when changing the refreshToken function to return this.http.get observer instead of rxjs of operator.

It works correct with of, but as soon as i change to http.get return it never fires the get call and doesnt emit anything, nor goes to the next concat observer subscription.

Cant figure out why its not working. Any help appreciated.

interceptor file:

export class CaughtInterceptor implements HttpInterceptor {
  constructor(private authService: AuthService) {
  }

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    let retries = 0;
    return this.authService.token$.pipe(
      map(token => req.clone({ setHeaders: { Authorization: `Bearer ${token}` } })),
      concatMap(authReq => next.handle(authReq)),
      // Catch the 401 and handle it by refreshing the token and restarting the chain
      // (where a new subscription to this.auth.token will get the latest token).
      catchError((err, restart) => {
        // If the request is unauthorized, try refreshing the token before restarting.
        if (err.status === 401 && retries === 0) {
          retries++;

          return concat(this.authService.refreshToken$, restart);
        }

        if (retries > 0) {
          this.authService.logout();
        }

        return throwError(err);
      })
    );
  }
}

auth.service file:

export interface RefreshTokenResult {
  accessToken: string;
}

@Injectable()
export class AuthService {
  private tokenSubject$ = new BehaviorSubject<string | null>(null);

  token$ = this.tokenSubject$.pipe(
    filter((token) => token !== null),
    take(1)
  );

  refreshToken$: Observable<any> = defer(() => {
    if (this.tokenSubject$.value === null) {
      return this.token$;
    }
    // Defer allows us to easily execute some action when the Observable
    // is subscribed. Here, we set the current token to `null` until the
    // refresh operation is complete. This ensures no requests will be
    // sent with a known bad token.
    this.tokenSubject$.next(null);

    return this.refreshToken().pipe(
      tap((res) => {
        this.tokenSubject$.next(res.accessToken);
      }),
      catchError((err) => {
        this.logout();
        throw err;
      })
    );
  });

  get token(): string | null {
    return this.tokenSubject$.value;
  }

  authenticate(): void {
    this.tokenSubject$.next('someToken');
  }

  refreshToken(): Observable<RefreshTokenResult> {
    // Changed of to this.http.get here
    return this.http.get<RefreshTokenResult>(url).pipe(delay(0));

      /*
      return of({
        accessToken: 'newToken',
      }).pipe(
        delay(0),
      );
      */
  }

  logout(): void {}
}
  • Any console errors? Maybe you were omitting it for brevity, but I don't see where you are injecting `http` into auth.service so that you can use this.http – Eric Phillips May 07 '21 at 22:31

1 Answers1

0

Observables returned by the HttpClient are cold observables. This means, the request will only be made, when you subscribe to that observable, or a piped one. I'm not sure where you need to subscribe but that's the reason that no request is made.

David B.
  • 695
  • 5
  • 10
  • Yea must be it, thought concat would subcribe but it doesnt with the httpclient cold observable. I refactored and went with different solution cause code was feeling messy at the end. Appreciate your help. – Andrés Aquino May 08 '21 at 11:07