Handling HTTP Requests with Authorization Using Interceptor in Angular
December 24, 2018
To build an enterprise platform, one inevitable problem is handling HTTP requests with authorizations. The HTTP requests are stateless, which means that HTTP requests do not hold the state of users’ login status. We need to add something in the requests so that the server would know the users have already logged in, which is Authorization attribute in the HTTP header.
The practice in industry is to generate a hashed token in the server every time users login and return this token to the client. Then every time when the clients send HTTP requests, the tokens are sent within the HTTP header, based on which, the server would know whether the users have logged in and who this user is.
Now the problem is, how do we configure Angular so that every requests sent carry the authorization attribute in the header?
The answer is using Interceptor.
Create an Interceptor
Intercept
A very simple interceptor can be created as the following code snippet shows:
@Injectable()
export class TokenInterceptor implements HttpInterceptor {
constructor(
private authService: AuthService
) {}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (!!this.authService.getToken()) {
request = request.clone({
setHeaders: {
Authorization: `Bearer ${this.authService.getToken()}`
}
});
}
return next.handle(request)
}
}
The class implements an interface called HttpInterceptor
, with AuthService
injected as a dependency which provides a method, getToken()
to check if a token has already been stored in the app.
In this class, a method intercept(request: HttpRequest<any>, next: HttpHandler)
must be implemented. The main function of it is to allow you doing anything before any request is sent.
So, in this method, we firstly check if the user has logged in by checking if the token exists. If not so, then it is unnecessary to add Authorization Header anymore. If it exists, then we proceed to update the request header.
Take note that we use request.clone()
to clone a new request and set its header as it is suggested by Angular official website.
HttpRequest represents an outgoing request, including URL, method, headers, body, and other request configuration options. Instances should be assumed to be immutable. To modify a HttpRequest, the clone method should be used. Angular - HttpRequest
Finally let the HttpHandler handle the updated request.
Handling Unauthorized Cases
Sometimes when the requests returns 401 Unauthorized
errors, that is mainly caused by accessing a privileged API without a valid token present. In this case, we want to redirect the users back to login page.
We will achieve this feature as the following code snippet shows:
return next.handle(request).pipe(
tap(null, error => {
if (error.status === 401) {
this.authService.logout();
this.router.navigate(["/login"]);
}
})
);
In this case, tap
operator in RxJs is used. tap
helps us to transparently do something we want when the requests show errors without affecting the main programme running.
In this case, we only want to handle errors, so that the first parameter is left to be null. Instead, we only check errors, if the error status is 401, that means it is unauthorized. Then, we call logout()
to force the application logout and clear unnecessary auth information in the app then redirect users to login page by using router.navigate()
.
In this part, you may also handling other conditions such as 500 Internal Error
or 400 Bad Request
.
Final Version
A complete Token Interceptor
is presented as below:
import { Injectable } from '@angular/core';
import {
HttpRequest,
HttpHandler,
HttpEvent,
HttpInterceptor,
} from '@angular/common/http';
import { AuthService } from './../services/auth/auth.service';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
import { Router } from '@angular/router';
@Injectable()
export class TokenInterceptor implements HttpInterceptor {
constructor(
private authService: AuthService,
private router: Router
) {}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (!!this.authService.getToken()) {
request = request.clone({
setHeaders: {
Authorization: `Bearer ${this.authService.getToken()}`
}
});
}
return next.handle(request)
.pipe(
tap(null,
error => {
if(error.status === 401) {
this.authService.logout();
this.router.navigate(['/login']);
}
})
);
}
}
Inject Interceptor into App
Now our interceptor is ready to be injected into the application.
In app.module.ts
, simply modify your @NgModule
as below:
// Your other imports
import { HTTP_INTERCEPTORS } from "@angular/common/http";
import { TokenInterceptor } from "./core/interceptors/token.interceptor";
@NgModule({
imports: [
// Your modules
],
declarations: [
// Your components
],
providers: [
// Your other services
{
provide: HTTP_INTERCEPTORS,
useClass: TokenInterceptor,
multi: true
}
],
bootstrap: [
// Your bootstraps
]
})
export class AppModule {}
Summary
This concludes how to handle HTTP requests with authorization using interceptors in Angular. Merry Christmas and have fun XD!