Photo by Simon Hurry on Unsplash
With Angular, you don’t have to add the Bearer authorization token to the header of every API call manually. Instead you can use an HttpInterceptor , which allows you to intercept every HTTP request and modify it before passing it through to the rest of the pipeline. Let’s take a look.
Adding an HttpInterceptor
You can use the CLI to create an interceptor with the following command:
ng g interceptor auth
This creates an auth.interceptor.ts file with a single method:
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
constructor() {}
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
return next.handle(request);
}
}
You’re meant to modify the HTTP request as needed, and then pass it through to the next handler in the pipeline. We’ll come back to this in a moment. For now just note that it returns an Observable.
Next we need to let Angular know to plug our interceptor into the request pipeline. In app.module.ts, add this new interceptor class to the providers array:
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true },
]
Now the intercept method of AuthInterceptor will be called on every HTTP request. But it doesn’t yet do anything — let’s fix that.
Adding the User’s Bearer Token to the HTTP Request Header
We want to add an Authorization header that contains the Bearer token of the currently logged in User. If you use NgRx in your application, then there’s a good chance that token is stored in NgRx state. Since the intercept method returns an Observable, we can use the mergeMap operator to get the token from state and then modify the request to add it as an HTTP header. Change the intercept method to look like this:
intercept(req: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
if (this.appConfig.apiHost &&
req.url.startsWith(this.appConfig.apiHost)) {
return this.store.select(userFeature.selectCurrentUser).pipe(
first(),
mergeMap((currentUser) => {
if (currentUser) {
const authReq = req.clone({
setHeaders: {
Authorization: `Bearer ${currentUser.authToken}`
},
});
return next.handle(authReq);
}
return next.handle(req);
})
);
}
return next.handle(req);
}
Let’s break this down.
First, we want to make sure that this request is going to our API host. Otherwise, not only is the token not needed, but we don’t want to send the Bearer token anywhere other than our API. So if the request URL does not start with our API host, we just pass it through unchanged.
Next we select the current User from state with this.store.select. Note the return keyword. Recall that we need to return an Observable, so we’ll project the Observable returned by this.store.select into the Observable that this method returns.
We use the first operator because we only need the first value.
Next we use the mergeMap operator to project (and flatten) the Observable that gives us currentUser, into the Observable returned by next.handle(req).
Inside mergeMap, if we don’t have the User in state for some reason, we just pass the request through unchanged. Otherwise, we clone the request and add the Authorization header to it. Then we pass the cloned request on to the next handler in the pipeline.
Now every call to our API will have the User’s bearer token added to the HTTP Authorization header.
In a future post, we’ll look at how we can unit test this interceptor.
Top comments (0)