Keeping your API tokens fresh
In a world where your data has to be accessed from multiple platforms, your APIs become first class citizens. Modern API security is important when you build real world apps that run on servers, mobile, web etc. There are so many aspects of security to consider, from infrastructure to frameworks.
It’s often cumbersome and requires meticulous planning, implementation and maintenance because of its cross cutting nature throughout your system. One wrong move and suddenly your company is enjoying the front row seats in public humiliation train. And you don’t want that ride…
OAuth 2.0
OAuth 2.0 emerged as a second iteration of the security framework endorsed by big names like Facebook, Google and Microsoft and it set out to standardise how API access delegation would work, i.e. how other services could access your data on your behalf.
You’ve seen this when you log in or register with your Facebook or Google account. OAuth2 never aimed to be a complete solution, it just laid the grounds and best practices to achieve API security standardisation. So some gaps had to be filled in, and among them were Bearer tokens.
Bearer tokens are used to authorize requests to protected resources and to quote RFC spec they are “a string representing an access authorization issued to the client“, the main idea was to remove user’s credentials from authorization headers and instead issue a token which would replace user’s credentials.
So now all you had to do to access user’s data is to obtain his access token and attach it to any request to protected resources. Now we have an authorization mechanism which didn’t include user’s credentials and could represent a set of user’s claims in a secure and compact manner.
As any security expert will tell you, changing passwords is important, they should expire often so that you limit the window of opportunity to wannabe password crackers. People genuinely love changing passwords, see the following google popularity suggestions:
If you’ve just lost faith in humanity, don’t worry. There is a better way, one which doesn’t force user to change passwords and it brings us other benefits too. We could create a long lived “master” token which could then be exchanged for the new access token every time the access token expires.
Also, you could request different access tokens to access different APIs and you could dynamically change user’s set of claims (remember they are stored in token). If security is breached this token can be blacklisted and denied access.
This is, in essence, the idea behind refresh token flow which you can read about in detail here (also check this great article). To understand this, it’s best to take a look at the diagram, and I’m a huge fan of the awesome RFC ASCII art diagrams.
So to force a client to get a new token you would just invalidate it server-side and client will automatically get a new one. Well… not exactly, the client will have to do it manually on each request, not what we hoped for. Let’s see an example…
Fetching data
Now that we’ve covered theory it’s time to jump to code. In modern apps you’d ditch the old XMLHttpRequest and instead use the new fancy fetch api. To follow the spec, and let’s assume you’d like to request a list of tweets, you’d probably write something like this:
fetch(‘https://api.twitter.com/1.1/search/tweets.json', {
headers: {
authorization: ‘Bearer your-access-token’
}
}).then(response => {
if (response.status === 401) {
// try getting the new access token and repeat the same request
}
// otherwise carry on
})
This is OK if you have one request, but in the long run, as your app grows, and you add more and more fetch requests, it becomes unmaintainable. You’re duplicating this logic all across the app on every request. Even if you extract it to utility methods it still repeats all over the place. There should be a better way…
Fetch token interceptor
We thought so too… What if we could enhance the fetch behavior somehow to automate this? We set out with this idea, and managed to create a library that does just that. It will intercept fetch requests and if they fail to authorize, it will try to renew the access token and retry the request.
We’ve packaged this into a utility library, for which you can find the whole source code on our Github repository.
All parts of this library are configurable, you only have to tell it how to authorize your requests and how to renew the token when it’s rejected, it will do the rest on it’s own.
This is a simple example of configuration object:
config: {
// specify if request should be intercepted here we intercept
// all requests
shouldIntercept: (request) => true,
// add authorization to request
authorizeRequest: (request, accessToken) => {
request.headers.set(‘Authorization’, `Bearer ${accessToken}`);
return request;
},
// create a request for renewing access token
createAccessTokenRequest: (refreshToken) => {
return new Request(‘example.com/v1/auth/tokens’), {
headers: {
Authorization: `Bearer ${refreshToken}`,
},
method: ‘POST’,
});
},
// extract the new access token from response
parseAccessToken: (response) => {
return response.clone().json().then(json => json.accessToken);
},
}
And when you configure it, all that is left to do is register it globally to change fetch behavior.
import {
configure,
authorize,
} from ‘@shoutem/fetch-token-intercept’;…
configure(configuration);
// here you provide tokens obtained from your Authorization Server
authorize(refreshToken, accessToken);
…
The library will now intercept the requests and authorize them accordingly. It will also renew the token when it expires and retry the operation. Now you can write all your fetch requests without polluting them with all this authorization logic.
Here is an example from Chrome Developer Tools showing it at work:
Here we can see that the first request failed with 401 status indicating that the access token is invalid. If we inspect it in more detail we can see that it had token1 in Authorization header.
Token interceptor went ahead and used refresh token to obtain a new access token.
After that, the initial request was repeated with the new access token token2 which resolved successfully. You can check and see that it was authorized with the newly fetched token.
We invite you to try it out and use Github to reach out with any questions!
Acknowledgements
I’d like to thank Vedran Ivanac and Željko Rumenjak for providing invaluable feedback and reviews during the development of this library.
I work at Shoutem where I help creating tools to supercharge your React Native development.