My Answer
In this case just handler 401
@Injectable()
export class AuthHttpInterceptor implements HttpInterceptor {
logoutUser$ = defer(() => (this.authService.logout(), EMPTY));
refresh$ = defer(() => this.authService.refreshTokenFromServer()).pipe(catchError(() => this.logoutUser$), share());
constructor(private authService: AuthService) { }
private applyCredentials(request: HttpRequest<any>): HttpRequest<any> {
return request.clone({
setHeaders: { Authorization: 'Bearer ' + this.authService.accessToken }
});
}
public intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (InterceptorSkipHeader.checkHeader(request)) {
const req = InterceptorSkipHeader.deleteHeader(request);
return next.handle(req);
}
const nextHandle$ = defer(() => next.handle(this.applyCredentials(request)));
return iif(() => this.authService.tokenIsEmpty, this.logoutUser$, nextHandle$).pipe(this.httpErrorsHandler());
}
httpErrorsHandler() {
return (source$: Observable<any>) => source$.pipe(
catch401Error(() => this.handle401Error(source$)),
catch400Error((err) => EMPTY),
catch403Error((err) => EMPTY),
catch406Error((err) => EMPTY),
catch500Error((err) => EMPTY),
);
}
handle401Error(retry$: Observable<any>): Observable<any> {
return retry$.pipe(
startWhen(this.refresh$),
takeUntil(this.authService.logout$),
catch401Error(() => this.logoutUser$),
);
}
}
full code ( auth-http-interceptor.ts )
step 1, Create two Observable
logoutUser$
:
- use
defer()
do your logout logic (like clear token from LocalStorage) and retun EMPTY
refresh$
:
use defer create refresh$
Observable, make it always take new refresh token to call refresh API
logout on catch error
share()
this Observable(make all 401 wait same refresh API back)
logoutUser$ = defer(() => (this.authService.logout(), EMPTY));
refresh$ = defer(() => this.authService.refreshTokenFromServer()).pipe(catchError(() => this.logoutUser$), share());
step 2, Skip interceptor
just make api skip interceptor ( uitls.ts )
class Xheader {
static readonly interceptorSkipHeader = new Xheader('interceptorSkipHeader');
readonly headers = { [this.headerName]: this.headerName };
readonly options = { headers: this.headers };
private constructor(readonly headerName: string) { }
public checkHeader({ headers }: HttpRequest<any>) {
return headers.has(this.headerName);
}
public deleteHeader(request: HttpRequest<any>) {
return request.clone({ headers: request.headers.delete(this.headerName) });
}
}
export const InterceptorSkipHeader = Xheader.interceptorSkipHeader;
like this InterceptorSkipHeader.options
( auth.service.ts)
refreshTokenFromServer(): Observable<Token> {
return this.http.post<Token>(this.authApi + '/refreshToken', this.token, InterceptorSkipHeader.options).pipe(setTokenToLocalStorage());
}
step 3, Interceptor

Has skip header InterceptorSkipHeader.checkHeader(request)
- delete and return without handler
Else, handler
- create
nextHandle$
with access token : applyCredentials(request)
use defer()
( always take new access token )
- use
iif()
check if token is empty will logoutUser$
, else nextHandle$
- add
httpErrorsHandler()
operator, handler this stream
public intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (InterceptorSkipHeader.checkHeader(request)) {
const req = InterceptorSkipHeader.deleteHeader(request);
return next.handle(req);
}
const nextHandle$ = defer(() => next.handle(this.applyCredentials(request)));
return iif(() => this.authService.tokenIsEmpty, this.logoutUser$, nextHandle$).pipe(this.httpErrorsHandler());
}
Add access token function
private applyCredentials(request: HttpRequest<any>): HttpRequest<any> {
return request.clone({
setHeaders: { Authorization: 'Bearer ' + this.authService.accessToken }
});
}
step 4, Custom operator
We should create some custom operator before error Handler
catchHttpError operator
In this case we just handler 401
catch401Error
: catch http 401
catch400Error
: catch http 400
catch403Error
: catch http 403
catch406Error
: catch http 406
catch500Error
: catch http 500
function catchHttpError(...status: Array<number>) {
const statusMap = status.reduce((m, v) => m.set(v, v), new Map());
return (next: (err: HttpErrorResponse) => Observable<any>) => {
return catchError((err) => err instanceof HttpErrorResponse && statusMap.has(err.status) ? next(err) : throwError(err));
};
}
const catch401Error = catchHttpError(401);
const catch400Error = catchHttpError(400);
const catch403Error = catchHttpError(403);
const catch406Error = catchHttpError(406);
const catch500Error = catchHttpError(500);
startWhen operator (uitls.ts)
equal delayWhen()
second parameter (subscriptionDelay)
export function startWhen<T>(subscriptionDelay: Observable<any>) {
return (source$: Observable<T>) => concat(subscriptionDelay.pipe(take(1), ignoreElements()), source$);
}
step 5, Http error handler

In this case we just handler 401
catch401Error must be the first (make sure other error handler will catch retry API error)
handle401Error(source$)
will retry source$
(previous Observable)
httpErrorsHandler() {
return (source$: Observable<any>) => source$.pipe(
catch401Error(() => this.handle401Error(source$)),
catch400Error((err) => EMPTY),
catch403Error((err) => EMPTY),
catch406Error((err) => EMPTY),
catch500Error((err) => EMPTY),
);
}
handle401Error
startWhen()
: retry$
will wait refresh$
complete than call retry API
- In process, if
authService.logout$
trigger will stop stream (unsubscribe)
- If retry API still 401 error will logout user
handle401Error(retry$: Observable<any>): Observable<any> {
return retry$.pipe(
startWhen(this.refresh$),
takeUntil(this.authService.logout$),
catch401Error(() => this.logoutUser$),
);
}
https://medium.com/@eddylin1937/angular-interceptor-with-rxjs-refresh-token-176326c84a36