Async HTTP Interceptors with Angular 4.3

The recently released Angular v4.3.0 brought with it a new HttpClient API. One of the most useful — and long overdue — features in this API is the HttpInterceptor interface, which allows us to globally intercept and transform HTTP requests. Interceptors have long been available in AngularJS and they are particularly useful for things like authentication, where it is often necessary to include an auth token in each request.
Prior to the release of HttpInterceptor we would create a wrapper service for Angular’s built-in HttpModule that our app would interact with, instead of calling the HttpModule methods directly. It all felt a bit cumbersome.
Now we can use HttpClientModule directly and simply plug in our interceptor as middleware in the pipeline. Nice and clean.
I had to implement an HttpInterceptor yesterday but I encountered a problem. The app made an immediate HTTP request to fetch the user’s profile and this request had to include the auth token to authenticate correctly, however the Bearer token was only available asynchronously from a Promise. This meant I needed to somehow pause the user profile HTTP request until the token was available.
It wasn’t immediately obvious to me how to achieve this — and the docs weren’t very helpful — so I thought I’d document it here. Perhaps it’ll be helpful to somebody else. And no doubt I’ll have forgotten how to do it by the time I start a new project — even though it is very simple!
Note: This tutorial only focuses on creating an asynchronous HttpInterceptor to fit into an existing project. As such everything else will be a bit vague.
The AuthService
We’re using IdentityModel’s oidc-client to provide OpenID Connect (OIDC) and OAuth2 protocol support, but you could be using anything — it’s not really important. The key is that the User object — which contains our access token — is only available asynchronously. I suspect this will be a common situation across many providers.
auth.service.ts
The HttpInterceptor
Now let’s set up our basic HttpInterceptor.
token.interceptor.ts
This is ready to intercept requests, but it won’t do anything special with them — they’ll just pass straight through and get logged to the console on the way. It won’t do anything yet though as Angular doesn’t know it exists.
Adding your Interceptor to the request pipeline
We must register the interceptor with Angular.
app.module.ts
Make sure that the AuthService and TokenInterceptor are imported into your AppModule. You need to provide the TokenInterceptor as an HTTP_INTERCEPTOR so that Angular knows to include it in the request pipeline as middleware.
app.component.ts
For clarity, here is our AppComponent that makes a request as soon as it is initialised. At the moment this request fails with a 401 Unauthorized status because the request doesn’t (yet) include the auth token.
Making the HttpInterceptor asynchronous
Let’s focus on the interceptor’s intercept method for a moment. As you can see it’s signature includes an HttpHandler called next as the second argument, much like you see in Express.js.
This threw me because I saw this and my brain went into Node.js-mode and assumed that next would function like a callback that you could call when you are ready to continue. No need to return anything. A bit like this:
Except it doesn’t work like that. Check the return type and you’ll see that it has to return an Observable, specifically: Observable<HttpEvent<any>>
Ok, but how can I return when I have to wait for a Promise to resolve?
Well, the trick is to convert the Promise into an Observable first. We can do that directly in our AuthService like this:
auth.service.ts
First we need to import a couple of things from RxJS:
Then we can simply wrap our Promise in the fromPromise method:
Now when we call getUser() we will get an Observable instead of a Promise.
token.interceptor.ts
Back in our interceptor. Again we need to import something from RxJS first. This time we need the mergeMap operator. What does mergeMap do exactly?
Projects each source value to an Observable which is merged in the output Observable.
In our simple scenario that means it will allow us to wait for and get hold of the resolved value, i.e. the User object, and use it to modify the request before we call return next.handle(request);. The two Observables, one returned by getUser() and the other returned by next.handle(), are merged together into a single Observable.
First the import:
Then we can refactor the intercept method to wait for the User before continuing with the request:
And that’s it. You have an asynchronous HttpInterceptor. Simple really.
Finally, to complete the picture, let’s actually add the elusive auth token to our requests.
To modify a HttpRequest you need to clone it. Handily clone allows you to specify the settings you wish to change so the cloned request will be ready to use straight away. If our User object exists we want to use the access_token property as a Bearer token in an Authorization header.
We can use ES6 template literals to keep it neat:
Note: Although the name setHeaders suggests it might replace all existing headers with those defined in the object it actually appears to merge them. New ones are appended, existing ones are overwritten. This is helpful as it means you can have multiple interceptors each adding their own headers without having to manually merge them.
Conclusion
So actually it’s not very complicated at all, once you realise you can use the power of RxJS.
- Use RxJS to convert the
Promiseinto anObservable - Use
mergeMapto combine the sources into a singleObservable - Remember to
clonethe request when modifying your request
I’ve made gists of the final versions of the AuthService and TokenInterceptor so you can double-check your implementation. You’ll just need to configure the UserManager or implement your own / another provider.
As a bonus I’ve added an error handler in the interceptor that catches 401 Unauthorized statuses and use’s the UserManager to redirect the user to the login.
Photo by Jamison McAndie on Unsplash
