Learning About HTTP interceptor with Angular
Posted By : Puneet Sharma | 17-Nov-2018
One of the very good new features in the angular 4.3 is the Httpinterceptor. SO We can add header information to call all new HTTP clients, control responses, capture errors, etc. This fun new material is available for import at '@angular/common/http'. This post is not in an intensive study of how to do an HttpInterceptor. Instead, it will include a way to update the OAuth authentication token using the refresh token in HttpInterceptor.
First of all, an explanation of what is happening with OAuth and Refresh Tokens. When you login, you receive an authorization token and refresh token. The refresh token is configured with a fixed timeout. So when that timeout arrives (despite the activity) you get a 401 (unauthorized) error with your API call. At that point, your code should try to refresh the token by calling OAuth Refresh Token Endpoint (with the refresh token string). This gives you a new authorization token and a new refresh token. Since then, you use the new authorization token for calling your API.
The HttpInterceptor is perfect for this requirement. All this you can add authorization headers to the http call and hold exceptions to hear 401 errors. When a 401 error occurs, it can call refresh token and can retry with the new authorization token.
export class testComponent { message: string; constructor(private httpService: httpService, private authService: AuthService) { } test401() { this.httpService.getData() .subscribe((response: any) => { this.message = `status = ${response.status}`; }, error => this.message = `status = ${error.status}`); } testMultiple() { let dataObservable = this.httpService.getData(); let lookupObservable = this.httpService.getLookup(); Observable.forkJoin(dataObservable, lookupObservable) .subscribe(([dataResult, lookupResult]) => { this.message = `status = ${dataResult.status} and getLookup status = ${lookupResult.status}`; }, error => this.message = `status = ${error.status}`); } }
Authentication Service
Authentication Service will give us a token. Here's my example code:
import { Observable } from 'rxjs/Observable'; import 'rxjs/add/observable/of'; import 'rxjs/add/operator/delay'; export class AuthService { public authToken: string = 'stale_auth_token'; public authTokens: string = 'new_auth_token'; public currentTokens: string; constructor() { this.currentTokens = this.authToken; } getAuthenticationToken() { return this.currentTokens; } refreshTokens(): Observable{ this.currentTokens = this.authTokens; return Observable.of(this.authTokens).delay(200); } }
HttpInterceptor
The Request Interceptor Service will implement HttpInterceptor which is the only one method: intercept. It will add a token to the header on each call and catch any errors that may occur.
@Injectable() export class interceptorService implements HttpInterceptor { isNewToken: boolean = false; istokenSubject: BehaviorSubject= new BehaviorSubject (null); constructor(private authService: AuthService) {} addToken(request: HttpRequest , token: string): HttpRequest { return request.clone({ setHeaders: { Authorization: 'auth ' + token }}) }
401 unauthorized handling
The code is the most important to handle 401 errors.
handle401Error(req: HttpRequest, next: HttpHandler) { if (!this.isRefreshingToken) { this.isRefreshingToken = true; this.istokenSubject.next(null); return this.authService.refreshTokens() .switchMap((newToken: string) => { if (newToken) { this.istokenSubject.next(newToken); return next.handle(this.addToken(this.getNewRequests(req), newToken)); } return this.logoutUsers(); }) .catch(error => { return this.logoutUsers(); }) .finally(() => { this.isRefreshingToken = false; }); } else { return this.istokenSubject .filter(token => token != null) .take(1) .switchMap(token => { return next.handle(this.addToken(this.getNewRequests(req), token)); }); } } getNewRequests(req: HttpRequest ): HttpRequest { if (req.url.indexOf('getData') > 0) { return new HttpRequest('GET', 'http://private-4002d-testerrorresponses.apiary-mock.com/getData'); } return new HttpRequest('GET', 'http://private-4002d-testerrorresponses.apiary-mock.com/getLookup'); } logoutUsers() { return Observable.throw(""); }
What will happen if the refresh token is out?
Something might be thinking at this point what will happen at the time of the refresh token. Generally, due to an API call, the timeout is configured due to it. Well, what happens is that you should see 400 error with the message 'invalid_grants'. Handle 400 error code is likely to cause the user to log out and direct to the login page. Here is the code that examines this situation:
handleError400(err) { if (err && err.status === 400 && err.error && err.error.error === 'invalid_grant') { return this.logoutUsers(); } return Observable.throw(error); }
The HttpInterceptor is a new feature in Angular 4.3 which is very powerful and useful in all applications. As you can see, it nicely handles "Refresh the token" behind the scenes so that the user, everything works smoothly.
Request for Proposal
Cookies are important to the proper functioning of a site. To improve your experience, we use cookies to remember log-in details and provide secure log-in, collect statistics to optimize site functionality, and deliver content tailored to your interests. Click Agree and Proceed to accept cookies and go directly to the site or click on View Cookie Settings to see detailed descriptions of the types of cookies and choose whether to accept certain cookies while on the site.
About Author
Puneet Sharma
Puneet is an experienced Lead UI Developer, having good knowledge of Angular Js, TypeScript, HTML5, CSS3, Javascript, Jquery, Photoshop.