In this blog post, I’m going to introduce the JWT (pronounced “jot”) token concept. I’m going to use the code presented in this article (click here) to demonstrate how a token is created and then I’ll discuss the parts of the token and how it is used.
Using the article code, I created a dumb and dirty console app to generate a token with this final code:
var token = new JwtTokenBuilder() .AddSecurityKey(JwtSecurityKey.Create("fiver-secret-key")) .AddSubject("james bond") .AddIssuer("Fiver.Security.Bearer") .AddAudience("Fiver.Security.Bearer") .AddClaim("MembershipId", "111") .AddExpiry(1) .Build();
For a real system, you’ll need to store your secret key and you’ll need to make it much larger. The other variables will differ according to your use of the token. Before I go into any details on how you would generate and consume the token, I’m going to discuss what the token looks like and how it works. First, I generated a token from the above code and obtained this result:
If you go to the wiki page on the JWT Token (JSON Web Token), you’ll see a description of the three parts of the token. If you look closely you can see that there are two dots “.” and they separate the three parts of the token. Each part is a base 64 encoded string so it is safe to transmit over the web. The first part is the header. If you cut the first part you’ll get this:
Paste that code into a decoder (click here for an on-line base 64 string decoder), you’ll get this result:
If you dig into the JWT token build method, you’ll see a section where HS256 hashing is used for the signature and this token is a JWT type token.
Take the next block of text (which is long) and copy it (without the dots). Then put it in the decoder and click on “Decode”. You should see this:
This is the payload section and you can see all the information that was placed into C# the code earlier.
If you’ve ever used tokens for accessing APIs, you’re probably thinking to yourself: What prevents someone from getting a copy of this token and just changing the information to something else and stealing admin rights or unauthorized data from the API? That’s where the signature comes in. The signature is the last part of the token. The signature represents everything that is in the header and payload, encoded with a secret key and formed into a hash. The receiver of this token must know the secret key in order to verify the signature. If anything in the header or payload is altered, the recalculation of the hash will result in a different signature value.
Keep in mind, that the JWT token should not be used to store anything you want to keep secret. So don’t put your password into the token payload and then expect it to be safe. Technically, it will be safe from packet sniffers because you should be accessing an API using SSL, but don’t assume that the payload section is encrypted.
If you were to generate another token after the expire time (in my example the expire was set to 1 minute), then the signature will be different. That’s because the token is time sensitive. This improves the security, in case the token is intercepted by a third party. It also prevents a user from reusing the token forever, instead of re-authenticating. Finally, it prevents a valid token from being altered to obtain data not specified by the original token.
Re-authenticating? Oh yes. There needs to be some sort of authentication scheme that allows the user to obtain a token by logging in someplace. This is up to the system designer to decide how it is accomplished. There may be a login id and password that the user uses to get a temporary key and access an API by hand (usually using an interface like Swashbuckle/Swagger). For machine-to-machine communications other protocols can be used. A token can also be issued for a longer time period for client use.
The token use can be narrowed using the issuer and audience. If your company has many APIs that are used for different purposes, you might issue a token that is restricted for use on one API. You can setup your API to reject any token that is not issued by your security API and you can reject tokens where the audience in the token is not serviced by the API.
Let’s say that you want to communicate from one API to another, how would you use the JWT token?
To use the token, you need to put it in the authorization header of the API call:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
When you grab the token from your cache, check the expire and replace the token if it only has a couple of minutes left. There could be network delays when sending your token to the remote API, even if it’s in the same data center. If you wait until the last second to renew the token, you’ll run into issues where the consuming API will reject your token. This problem will increase when your network is experiencing an unexpected latency.
For your consuming API, you must check the signature first. There is no need to accept a JWT token that has a bad signature. That just indicates that someone changed the payload data manually. Once that has been verified, then check the expire date/time and make sure the token is still valid. Next, you’ll want to check any other restrictions placed on the token (audience and issuer).
What Else is in the Token?
You can store data inside the token. There is no limit, but you should keep the token as small as possible. Don’t pass around a 10 megabyte token and expect a fast network. You should pass some authorization information of the person or machine that authenticated (i.e. user id, claims). By doing this, you can form a state-less website. Your front-end might keep the token in a cookie after the user logs in. That token is then used in the bearer authorization header for every call to your website back-end. This eliminates the need for a website session (i.e. session-less or state-less). For this use case, you can have the authenticated user’s id or some sort of temporary key contained in the payload (i.e. generate a new temporary key when the user logs in and store it in the user table, then throw it away when the user logs out).
The data that you store in the payload should be something that is used repeatedly by the APIs or website you are accessing. Otherwise, if you try to store something like the current page number in the JWT token, then a new token will have to be issued every time you click the “next” button to get a page of data. If you’re looking for a place to store temporary state variables, I would recommend putting them in a cache and keying the cache to the user id. Once the token has been authenticated, the user id can be used to grab the data from your cache and you can continue as though you had a session.
Why Use a Token?
When I create a new API, I have a check-list of what goes into the shell of the API:
- JWT Token authentication management for all calls
- Help screen (swashbuckle/swagger)
- IOC container
No Web API should be without these items. My use of the help screen allows me to document my API and gives me a method of testing the API directly. The logger will be necessary to troubleshoot problems and track exceptions once the API is deployed to an environment in the data center. The IOC container is the framework that allows developers to use unit tests with ease. Last, but not least, never deploy an API without some sort of security. Unless you want the entire world to be able to access it. Install and test your JWT token security first, then you can add controllers and business logic without worrying about a hacker accessing your data before your API goes live (I’m assuming that you might deploy an API for a feature that is turned off).
You might be thinking that your data is open to the public anyway. Why secure your API? You might want to limit access to your data via your website or mobile device interface. If your API is open to the world, some entity, could tap into your API without your knowledge and put a load on your system that you don’t expect. Don’t assume anything. A random Facebook developer could create an application that accesses your API and then a half-million Facebook users unwittingly hit your API when they use that app. Yikes!
If you want the world to have access to your API, then you better be prepared to scale.