Exchange refresh token for JSON Web Token (JWT) in AngularJS using Promise

Let’s say we have some REST APIs that provides resources to our AngularJS web application. These REST APIs happen to be secured with JWT. In this post, I don’t care how JWTs are generated or issued or validated. I just want to focus on how we need to write the AngularJS code in order to consume these secured REST APIs.

Easy right? Well that depends. I will show here 3 approaches. The first one is a naive approach and doesn’t work. The second one might seem like a better approach but still does not work. The third and final approach is the solution that we want.

Regardless of which approach, here are the common steps:

  1. When user logs in, the code saves the JWT (access token), refresh token, and expire time in memory and local storage. I will explain the reason we want to keep in both memory and local storage in step 3.
  2. For every HTTP request to the REST APIs, if there’s a JWT in memory, then checks the expire time (the memory copy). If the expire time has not expired, put that JWT in the Authorization header using the bearer scheme and send along with the requests. The header of the request would contain an Authorization attribute and look something like:
 Authorization: Bearer eYx….

3. If there’s no JWT in memory, fetch from local storage and do the same thing in step 2. The reason we must keep in both memory and local storage is because if the browser happens to be closed, what’s in memory will be gone. We don’t want users having to be challenged for credentials again when they return to our application. So in this case, we have to fetch from local storage first and update what’s stored in memory.

4. If the JWT already expires, then using the refresh token (either from memory or if not available there, then local storage), we’ll exchange for a new set of JWT and refresh token from the Authorization Server. Then we update this new set in memory and local storage and go through the steps above again.

As I mentioned, approach 1 is a naive one. In this approach, we write a service authService.getJWT() that returns a good and valid JWT. This service would have all the logic to figure out what it needs to return, either the JWT from memory, from local storage or what it gets as a result of exchanging the refresh token. The callers don’t have to care. Here’s what the callers would look like:

const promise = $http.get(uriEndpoint, { 
headers: {‘Authorization’: ‘Bearer ‘ + authService.getJWT() }
});

This doesn’t work because if the JWT expires, authService.getJWT() must make an async $http call to exchange the refresh token. But authService.getJWT() returns to the caller right away, and at that time of return, the future value of the $http call is not available yet, so the value returned is undefined.

The second approach uses AngularJS HTTP Interceptor. This didn’t work because of a circular dependency problem. The idea of an HTTP Interceptor is that for every request made by $http, the interceptor intercepts the request and modifies the request by adding the JWT in the Authorization attribute. This would make everything simpler and more convenient. That’s all fine and no problem except that if the JWT already expired, then we would have to make an HTTP request to the Authorization Server to exchange the refresh token. So we intercept the HTTP call to do some processing, and in that processing, we attempt to make another HTTP call. This is where the circular dependency problem happens.

We have to chain a pair of 2 promises. The way I think when I say “a chain of 2 promises” is like this: I have 2 consecutive async function calls where each returns a promise. The first async call returns a future value that is likely to be used as an argument to the second async call. So in this context, the first async function call returns a promise that when it resolves in the future, it will give a good and unexpired JWT. The second async function call just takes this JWT, put it in the request header of the $http call and returns a promise to its caller.

Nothing too surprising here. The only tricky part is in the above first function call of the chain. It doesn’t always have to perform an async call because sometimes it already has the JWT in memory or local storage that has not expired.

Fortunately, there’s $q.when() to help us. Essentially, we pass a normal JS object to $q.when(), it returns a promise, but the promise resolves immediately. So the authService would now look something like:

So in line 11 and 17, the function doesn’t have to perform any async operation, because it already has the JWT (from memory and local storage respectively). But the caller of this function still expects a promise to be returned. So we simply wrap the returned JS object in $q.when().

To form the chain that makes a request to a secured REST API, we can write a function as follow:

getSomeSecuredResource() {
return authService.getAuthorizationHeader().then(authHeader => {
return $http.get(uri, {headers: authHeader});
});
}

Written by

Driven by passion and patience. Read my shorter posts https://dev.to/codeprototype (possibly duplicated from here but not always)

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store