The ins and outs of JWTs
Tags: beginners, tutorial, security, azure
Published: 2021-10-18 12:43:44

If you learned something new feel free to connect with me on [linkedin](https://www.linkedin.com/in/albert-bennett/) or follow me on dev.to :)<br/>
Hi and welcome to my post. In this one I am going to go through what a JWT is, how to create one and manage it using Azure AD and Postman.<br/>
**Introduction**
Simply put a JWT (Json Web Token) is an encrypted json object that proves that the user is genuine. It can also contain a list of permissions on a resource that you can access using the token. Most tokens have an expiry attached to them, when this is up the token can no longer be used. 
They work on the bases that the user gives the authorization authority some information (username and password, client id/ secret, etc). These credentials are then passed on and exchanged for a token. Each library has it's own ways of doing this, so it isn't always necessary for the user to pass on this information each time that they want to access the resource. Especially if the has already signed-in to the resource (think Microsoft login page). Things can be a little different server-side in that permissions will have to be consented to by an admin, not the user.
Tokens are normally consumed by passing them in the authorization header of a Http request. Normally, not the rule though. Each API can manage it's authorization in different ways, and I have seen many ways of this being done. So, do plenty of research before hand on the API and it's authorization policies.<br/>
**Authentication Flows**
Like I had mentioned we are going to use Azure AD to establish our tokens. Before getting into it through it is worth going through OAuth 2.0 flows. Specifically the client credentials flow as this is what we are going to use to generate the JWT and it is the most common auth flow that I have seen being used. Essentially we are going to use a client ID and secret to generate a token, these details along with the scope are going to get passed on to the authorization server (Microsoft's MSAL server) to grant us a JWT. Here is a diagram of how this is going to fit together:
![Client credentials flow diagram](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/12urv9ifhloke4t3dx33.png)I'll go thorough how we can do this using Postman.
As a note there are 5 main flows when generating a JWT Those are:
- Client Credentials
- Implicit grant
- Authorization code grant
- Refresh
- Resource owner credentials grant

**Set-up**
Now onto Azure!
For this we will need to create two AD apps.
One is going to represent our front-end, from this AAD app we are going to be getting our client id and secret. The other one is going to represent the resource that we are trying to access, but this could easily be one of the many resources listed in the AAD app panel instead (more on that later).<br/>
**Backend AAD App**
We are going to start of with the back end app firstly because it is the easiest to set-up and secondly we need to associate it with our front-end app. After going to Azure Active Directory in Azure and then the 'App registration' tab. Here is the steps to take to set it up in Azure: ![backend app step 1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5hxst399wxwg4x5l9pgg.png) For the first step all we need to do is fill in the name of the app and click register. In other flows you might also need to fill in the redirect Uri as well, this is so that the token response can go back to the client.
Next we need to set the app id of the app registration this is to tie the scope to it. you can call it what you want but, I find that it is better to leave the App Id in it somewhere:
![backend app id](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m2hornx8m73ia9x7da57.png) Afterwards we need to add a scope to the app registration. It doesn't really matter what you call it. Something meaningful is always better.
![backend app scope](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iql8onnu7p0n7tuvjqu8.png) Next we need to create a new app role, this kind of acts like an activator for our permission as in it we are stating that this is role the that app is going to be representing. It is an easy form to fill out just make sure to give meaningful names and descriptions, also be sure to set the Allowed member types to applications because, that is what is going to be authenticating access the app role.![backend app step 2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0bggolzfcjng402hn18t.png) With that, click apply to finish setting up our app registration.<br/>
**Frontend AAD App**
It is a similar process to the backend app. ![frontend app step 1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uw9nytji1cmrcvdk4f1u.png) In our next step we need to grant access to our backend app. This is done by going to the request API permissions tab and then Add a permission. Now from this step you can see that there are many different permissions we can grant to our front end app from the MS graph and Azure, here is where you would set them meaning that you could skip the entire creation of the backend app if you wanted to use one or more of those instead, I'd recommend though that if you were to use them select the type as application permission instead of delegated because, after all we are using an app to authenticate not a user.![front end app permissions](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0mx33c8lzvn7vri84zhc.png)
In our situation we need to select our backend app, this is done by going to API's my organization uses and finding the backend app.![front end app permissions](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xkeiqdvosxglu42cyhq3.png)![front end app permissions](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ctc9c2tt9ar2dgmqm83t.png) Finally click add permissions and then grant admin consent if you are an admin, or get an admin to do it for you. ![front end app permissions grant admin consent](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kdyo18gmv9ci2yrn5d2q.png)![front end app permissions grant admin consent](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7rqnmgikohxsbqkip4ms.png) The last step to setting this app up is to create a new client secret. This is done by going to the Certificates & secrets tab and then creating a new secret. Also, try to set an expiry of it as well best practices dictate that you have an expiry of 6 months on secrets. ![front end app client secret](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nt0zqiavos30zqg02491.PNG)With that we are ready to start generating JWTs.<br/>
**Postman testing**
To test we need to make a post request to the authentication server with some info from our AAD apps.
What need is the following:
- Client ID from front end app registration:![Client id from front end app registration](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ehuk65uls9qlukxvn1by.PNG)

- Client Secret (value) also from front end app:![Client Secret also from front end app](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2bijkxk0itwegvmqo402.PNG)

- Scope from backend app:![scope from backend app](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aw71cc7f4mrq78bg2hfz.png) alternatively if that doesn't work using /.default should work instead of your permission name (in my case I needed to replace 'test-api'). I've shown this in the example.

- Token URL, this can be a bit tricky to find but shouldn't change as it is tied to your tenant and is formatted as: 
https://login.microsoftonline.com/[tenant id]/oauth2/v2.0/token
or
https://login.microsoftonline.com/common/oauth2/v2.0/token 

In postman our request looks like this:![Postman request](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kp69xzoedsdhfh6i13ql.PNG) Make sure that your request content type is set to application/x-www-form-urlencoded as this is what the authentication server is expecting.
or in cURL if you'd prefer:
```cURL
curl --location --request POST 'https://login.microsoftonline.com/[tenant id]/oauth2/v2.0/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=[your client id]' \
--data-urlencode 'client_secret=[your client secret]' \
--data-urlencode 'scope=[you app id]/.default' \
--data-urlencode 'grant_type=client_credentials'
```
And our response should look like this:
```json
{
    "token_type": "Bearer",
    "expires_in": 3599,
    "ext_expires_in": 3599,
    "access_token": "eyJ0eXAi..."
}
```
**Response Overview**
Although we have our token I think that we should take a look at the response from the authentication server.
- access_token: this is our access token in the next section we will decrypt it and see what is going on inside of it.
- token_type: this is going to tell us the schema of the access token it also lets us know how we are supposed to pass on the token. In our case if we wanted to use the access token we would need to send it to an API prefixed with the schema ergo the authorization header of our request would look like this: [schema] [access token] or bearer eyJ0eXAi...
- expires_in and ext_expires_in: in our example the value of this is an hour its the number of seconds you have from the time the token has been issued until it become invalid.

**Decrypting the token**
Finally lets take a look at what is going on inside of our access token and prove that what we had done actually would work. We can do this by going to https://jwt.io/ and pasting the token in the encoded section:![decrypted access token](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hlm5wsks95n4ij61b49o.PNG)If you try this out with a dummy token or one you generate yourself it is worth it to hover over all of the various decrypted properties in jwt.io just to see what is going on. 
You can see from examining the aud (audience), iss (issuer) and roles properties that what we have would let us access an API if it was associated with our backend AAD app as these values are present in either our frontend or backend app.<br/>
And with that we have gone over the ins and outs of JWTs, and how we can generate one using the client credentials flow.