Last active
November 25, 2020 16:36
-
-
Save pankajparkar/9c1b30b48b5caca82e319cc06844dd7d to your computer and use it in GitHub Desktop.
Final Token Interceptor
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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