Reauth in Angular Made Easy
Adhere to the OAuth 2.0 Spec Using Angular Interceptors
In the age of modern web apps, very often the first thing that users of an app are faced with is a login page. Logging in lets the application know that the user is who they say they are. The industry standard for online authorization is OAuth 2.0. Creators of front-end web applications need to learn how to adhere to the always-prevalent OAuth spec and keep the user experience seamless as they manage the identity verification process.
In this article, we will learn how to implement a reauthorization mechanism in Angular that operates behind the scenes, taking care of reauth automatically.
What is OAuth 2.0?
OAuth 2.0 is an open standard for online authorization. It allows online applications to access services using a user’s identity, without constantly sharing their username and password.
OAuth does not in itself establish your identity as a user but serves as a way of delegating that you are who you say you are to third-party applications after you have verified your identity in some other way.
OAuth does not replace the need for a username/password combination, or some other authorization method.
How Does OAuth Work?
At a high level, the OAuth 2.0 spec works in this way.
- A web application accepts some form of user credentials from you. Typically this is a username and password.
- The web application makes a request to an authentication server to receive an OAuth JWT token. This “auth token” is then used in place of your username/password combination in all future network transactions to show that you are who you say you are.
- Additionally, alongside your “auth token” you receive a “refresh token,” which, as the name suggests, is used to “refresh” your auth token when it inevitably expires.
In this article, we will learn how to implement the “token refresh” mechanism that is crucial to ensuring that your logged-in user is not immediately booted out of the application the second their auth token expires.
Note: This article assumes that you already have an OAuth mechanism in place for login and storing your authorization and refresh token in the application state. This article will only be a guide for refreshing an expired token. See the linked demo application at the end of the article for the full app example.
Angular Http Interceptor
The best way to interact with in-flight HTTP requests in Angular is to use an HttpInterceptor. This interceptor will contain a function that is run on every HTTP request that your application makes. Let’s make one.
This is our basic interceptor shell. We’ve created a class that implements the HttpInterceptor interface. This declares the intent of this class so that Angular knows it is an Interceptor.
The intercept method receives two parameters. The current in-flight request, and a next HttpHandler which has a handle function to be called when we are ready to let the request continue.
What Are We Intercepting?
As your Angular web application makes requests, passing along the OAuth token and receiving (hopefully) 200 OK status responses, eventually a request is going to error with a 401 Unauthorized response.This response code is sent when the server recognizes a valid request but refuses to authorize it. In our case, it is because the OAuth "auth token" is no longer valid for whatever reason. Assuming the token was once a valid token, it is likely because it is expired.
Our application needs to intercept a 401 Unauthorized error and retry the request with a new, valid token. We do not want to surface this error to the user, which is why we use an HttpInterceptor to intercept this error and handle it before the response is returned back to the initial requesting code.
Catch The Error
Let’s go back to our code, and add some error handling…
What? We are calling next.handle right away when we get a request? That's right! Unlike other interceptors that you may use, where you want to modify or check an outgoing request before it goes out, calling next.handle when you are done, here we are interested in what comes back to us. We want to pass the request on to the server, and then attach some RxJS operators to the call to next.handle and check the response.
If we get a 401 Unauthorization status from the response, we want to handle it. If the error code is anything else, we want to rethrow the error. This interceptor is only interested in handling one specific error. For any other error, the requestor needs to know about it and handle it themselves.
Note: A similar but different error code is a 403 Forbidden error. This is not the same as a 401 Unauthorized. A 403 Forbidden error means that your authorization token is valid (not expired), but that the token does not grant the requestor access to the requested resource. You do not and should not get a new token in this case. This is the OAuth auth token doing its job of preventing unauthorized requests from accessing protected resources.
Handle The Error
Here we are assuming an AuthApiService exists that is responsible for making calls to our authorization server to get tokens. To see more details about this implementation, please see the full demo application linked at the end of the article.
When we get a 401 Unauthorized error, we want to:
- Make a call to our authorization server to get a new auth token, using the refresh token we got from our initial login.
- Retry the request but with a valid Authorization header
We make a call to our AuthApiService to .getNewToken, giving it our 'refreshToken'. In your application, this will be a real token. When that value returns we want to switch to a new request using RxJS switchMap. This new request will be a clone of the first request but with new headers on it. We'll then call next.handle again with the new request, successfully retrying the request.
Something to note is that this interceptor will not loop infinitely. If this retried request again errors with a 401 Unauthorized, it will not be caught by itself again. It will skip this interceptor entirely and have its error response sent back to the requestor.
Hook Up That Interceptor
All that’s left now is to make Angular aware of our interceptor. In your AppModule, we need to add a new provider.
Adding this HTTP_INTERCEPTORS provider will add our interceptor to the application. Note, if you have more than one interceptor in your application, you will need to add multi: true to your new provider.
And there you have it, you now have a working refresh mechanism for your application that will catch unauthorized requests, get a new token, and retry the original request, all behind the scenes without any interruption to the user.
Please check out the full demo application linked here. It contains a mock server, an auth interceptor that will add the auth token to every outgoing request, and a full demo web app where you can view the process of reauth in real-time by viewing your browser’s network activity.
(Please note that the server portion of the demo app does not contain a real, working authorization server. It is not suitable for real-world authorization/authentication in any production application.)
Conclusion
Authorization is an important aspect of every modern web application, but it doesn’t have to be complicated. Using the power of Angular’s HttpInterceptor, the reactivity of RxJS operators, and the open standard of the OAuth 2.0 spec, you can ensure that your application handles reauth correctly, giving your valued app users a seamless and authenticated experience.