Skip to content

Instantly share code, notes, and snippets.

@pankajparkar
Last active November 25, 2020 16:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pankajparkar/9c1b30b48b5caca82e319cc06844dd7d to your computer and use it in GitHub Desktop.
Save pankajparkar/9c1b30b48b5caca82e319cc06844dd7d to your computer and use it in GitHub Desktop.
Final Token Interceptor
import { AuthService } from './auth.service';
import { Injectable } from '@angular/core';
import {
HttpRequest, HttpHandler, HttpInterceptor, HttpSentEvent,
HttpHeaderResponse, HttpProgressEvent, HttpResponse, HttpUserEvent
} from '@angular/common/http';
import { Observable, BehaviorSubject, throwError} from 'rxjs';
import { catchError, switchMap, finalize, filter, take, map } from 'rxjs/operators';
import { Router } from '@angular/router';
import { JwtHelperService } from '@auth0/angular-jwt';
const helper = new JwtHelperService();
// You can add URL which don't need AUTH header
const whiteListUrls = ['login', 'refreshToken'];
@Injectable()
export class CustomHttpInterceptorService implements HttpInterceptor {
constructor(
private auth: AuthService,
private router: Router) {}
isRefreshingToken = false;
// Observable use when refreshToken call is made
tokenSubject: BehaviorSubject<string> = new BehaviorSubject<string>(null);
// Check expiry of token, first decode token
// extract data, and verify expiry timing wrt currentTime
// should be less that currentTime
private isTokenExpired(token): boolean {
const decoded = token && helper.decodeToken(token);
const date = new Date().getTime();
return decoded && new Date(decoded.exp * 1000).getTime() <= date;
}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpSentEvent | HttpHeaderResponse | HttpProgressEvent |
HttpResponse<any> | HttpUserEvent<any> | any> {
const token = localStorage.get('access_token');
const refresh_token = localStorage.get('access_token');
const isExpired = this.isTokenExpired(token);
// Directly allowed whitelisted URL's
if (whiteListUrls.find(w => request.url.includes(w))) {
return next.handle(this.addTokenToRequest(request, token));
}
// isExpired does check expiry of token
// isRefresh check wheather refresh token is happening.
if (isExpired && !this.isRefreshingToken) {
this.isRefreshingToken = true;
this.tokenSubject.next(null);
// Start refresh token call
const refreshToken = this.auth.refreshToken(refresh_token).pipe(
// Retreive new refresh_token
catchError(_ => {
// Logout if refresh call fails
return this.logout() as any;
}),
switchMap((user: any) => {
// when new user token retrieved
if (user) {
this.auth.updateTokens(user);
this.tokenSubject.next(user.accessToken);
// Make the ajax call after by passing `accessToken`
return next.handle(this.addTokenToRequest(request, user.accessToken));
}
// Log out if there is no user
return this.logout() as any;
}),
finalize(() => {
// Once refreshToken call finishes turn off the flag.
this.isRefreshingToken = false;
})
);
return refreshToken as any;
} else {
// This observable helps other ajax call to wait until refresh token call completes
return this.tokenSubject
.pipe(
filter(accessToken => !this.isRefreshingToken && accessToken != null),
take(1),
switchMap(t => {
// Resume the pending calls those are waiting for refreshtoken call to finish
return next.handle(this.addTokenToRequest(request, t));
})
);
}
}
private addTokenToRequest(request: HttpRequest<any>, token: string): HttpRequest<any> {
return request.clone({ setHeaders: { Authorization: `Bearer ${token}`}});
}
private logout() {
this.auth.logout();
this.router.navigate(['login']);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment