When building an API and securing it with OAUTH, I want to fully test the functionality as an end user, so I need to provide a bearer token issued to that test user so that I validate all the functionality of my API. A bearer token only has limited validity (usually 1h) so I need a quick method to issue a bearer token and test with Postman, and make it repeatable. Fortunately, most of what you need is included in Postman itself.
I am working with Azure B2C in this scenario, although this would work mostly the same with Entra ID, or even other openid connect compliant identity solutions.
When connecting to an API, there are typically 2 scenarios that control how you deal with identity. End users don’t usually connect to an API directly.
- A service (daemon) connects to the API and fetches/submits data. If the daemon is NOT working on behalf of a user, the daemon will get an identity and will connect on its own behalf. This is a typical scenario of communications between services. The flow to be used is the ‘client credential’ flow and is NOT the subject of this article. With the app registration, the permissions configured are application permissions.
- A client application connects on behalf of a user. While the client app will request a token for the user, the permissions uses are those that apply to the user, not the client application. The permissions configured in the app registration are delegated permissions.
My Scenario
I needed to test as an actual user rather than a daemon because my API will provide a user-specific responses. I also want to ensure my configuration is correct. Initially, I used ‘implicit’ flow, but this is not recommended anymore, so instead we’ll use the authorization code with PKCE flow.
This is different from a scenario where you have another service accessing this API and connecting as that service (rather than on behalf of a specific user). That scenario is very different and for a different blog article. The flow in that scenario is called the “client credential” flow.
What are these flows you may ask? Well, OIDC and OAUTH support multiple different flows (patterns) that control how the authentication and authorization is implemented. A flow starts with a user attempting to access an application and ends with the token being issued. The flow controls every step along that journey, and what that journey looks like depends on the situation. In most cases, there’s only one appropriate flow. For example, the ‘client credential’ flow is typically used for two services to communicate with each other and will involve an AppId (the user Id of the application) and a secret (the password). A web browser accessing an app, vs a mobile app on phone will each be using a different flow. Awareness of which flow you are using will help you configure this correctly and troubleshoot, however you do not need to configure the individual steps. There’s some excellent documentation on Microsoft’s website explaining the differences between the flows.
How to set this up
Our test environment
Besides a working Azure B2C tenant (with user flows for sign up and sign in configured) and Postman, you don’t need anything else to follow this article. We are configuring an API app registration and a web app app registration. The web app registration is for Postman, the API registration for your API. You can follow this scenario even if you haven’t actually built your API yet, you’ll just have to follow in the console to see a token being issued. Hence, I am not focussing on my API itself, just on the configuration needed to make the authorization work.
Some background
I may write another blog to explain OAUTH and OpenID Connect (OIDC) in greater detail. However, for now here is what you need to know:
- OAUTH is a framework (standard) for authorization. Think of OAUTH as the standard being used here
- OAUTH handles authorization and is what the API will use
- OIDC handles the authentication. OIDC is built on top of OAUTH and is newer.
- OAUTH will expect a bearer token (also known as an Access Token, or Java Web Token, JWT). OAUTH defines the standard for that token and how to read it.
- Every party involved will need to be registered. So, we have an app registration for the API, for Postman, and one for the user (which is the user account).
- the token (JWT, JSON web token) is a bit of JSON that has been signed and Base64 encoded. They are trivial to read.
The process in a simplified way is as follows:
- user connects to a web application. The application requires the user to login
- the user is redirected to Azure B2C (a fairly standard HTTP 302) and goes through the authentication process.
- Azure B2C issues an authorization code.
- using this authorization code, the web app will connect to the Azure B2C token service and request a bearer token (the JWT).
- the app will connect to the API using the bearer token (in a header called ‘authorization’). The authorization header is included with each request.
In this particular case, Postman acts as the web app to make our life easier. However, when configuring a web app, you’d use mostly the same config, except the redirect URL will need to match what the app is using.
The components
- you’ll need an Azure B2C tenant. This scenario will not change based on whether or not you enable Facebook, Google or Azure AD federation. The access token is always issued by Azure AD B2C.
- you need an App Registration for the API
- you need an App Registration for the Web app / postman
Walkthrough
Create an App Registration for the API.
Note: the following is done within Azure B2C, the process is very similar to Azure AD but that’s not the focus of this article.
- In Azure B2C, go to App Registrations
- Select New registration
- Give this a name, such as demo API
- Select the supported account types. Select the 3rd option. The first option is for local accounts only (not very useful). So, select the 3rd option which allows the use of any identity provider configured in the user flow.
- There’s no need for a redirect URL, also uncheck the Grant admin consent….
- Click register
Take note of the Application ID. If you were to build an actual API, this Application Id will be used in the configuration of the API.
Next:
- Within the settings of your just created App Registration go to Expose an API.
- First you need to set an Application ID URI. Click on set. You can use the proposed value or create your own. This must be UNIQUE within the Entra ID environment. As you can see, the first portion is already filled out. Users will NOT see this value, but it is important for our configuration as without this, our web app (or postman) won’t be able to specify what it is asking approval for.
- Click on Add a scope. You need to create a new scope, for example “API.access”, specify the admin consent display name and description. Click Add Scope.
This last bit is one of the most confusing elements for people to grasp because they are not sure what all this means or what the proper values are. Essentially, what we are doing here is giving the API an identifier and creating a scope. OAUTH access tokens are issued for very specific targets and purposes. Which target and what purpose or scope is specified by the client when it requests the token. This is what the ‘scope’ is used for. This is a text value that you specify (and you decide what you want it to mean) and this text value will end up being included in the JWT that is issued. Azure AD B2C has several built in scope such as ‘profile’ which allows an application to get some details about the user (such as their name). For your own API, you will need to specify a new scope which can be as simple as “Access Api”. The name of the scope is something which is presented to the users, so it makes sense to pick something meaningful. When users login, they will be asked to consent to the app accessing that scope. So, make sure you provide a description which has meaning and makes sense. You need at least one scope, but can have more. The configuration in the App Registration doesn’t care which scopes you create or what they actually do. That’s something you configure within the API, if you want to. For example, you could create two scopes: API.read and API.write. Within the API, you can then implement different functionality, requiring this particular scope to be present before that activity is permitted. In our scenario, we are JUST specifing a name. If you want to call this Bambiclown, our examples would still work. The consent message may be a bit nonsensical in that case. The app needs to specify some scope when requesting the bearer token, because the bearer token is issued for a specific application. So using the scope, an app will request a token for a particular target and a particular scope. Think of it this way: when requesting a token, you need to specify for which API and for what purpose. That’s what the scope is used for.
Next to the scope, there’s a button to copy the scope value. Take note of that value, you’ll need it later in Postman. Should you forget, you can always come back.
Create an App Registration for the web app.
Next, we have to create an app registration for Postman (or a web app, the config is essentially the same).
- Within Azure B2C, go to App registrations, and click new registration
- Give this a name, such as “demo WEB”. Select the 3rd option for supported account types (it’s the default anyway)
- Specify a redirect Uri. If you don’t know what to add, just enter https://localhost. Select “web” as the application type.
- Make sure the “Grant admin consent…” is NOT SELECTED.
- Click register
Selecting Grant admin consent… is used to enable the implicit flow. This is still very common and works fine. However, it is not the most secure option and Microsoft now generally recommends against it. As we are using the authorization code flow, leave this unchecked. The implicit flow is still often used for single page apps (SPA) as they shouldn’t store any secret. That is not a problem when using Postman.
The redirect URI is a vital part for things to work properly. When using Postman, you can create any redirect URL you want, you will just have to ensure you use the same value here as you’ll use within Postman. When using an actual Web App, the value will need to match what is used by the Web App. During development, this will likely be something like https://localhost:43325, in production it’ll be the public URL. The actual value will depend on the Web App and the library you use for authentication. You can have many redirect URIs. When an app requests a token, it will specify the request URI in the outgoing request and has to match a configured value.
- Go to API permissions. As we didn’t select the Grant option, we will need to add two additional scopes. Click on Add a permission, select Microsoft Graph, select Delegated Permission, and select openid. This scope allows us to login using OIDC. You can also select the offline_access. This results in a refresh token being issued. We don’t strictly need this, but it’s interesting to see this happening.
What is this Delegated Permissions you may ask. Quite simple, we are telling the app (in this case Postman) to request a token on behalf of a user and thus get the permissions that apply to that user. If an app were to ask permissions for itself, we’d use application permissions. This is used for api to api communications and doesn’t apply here.
- Click on Add a permission again.
- In this case, select My APIs, and you’ll find our “demo API” in the list. Select that API, and then select the “API.access” scope from the list.
- Click on the grant admin consent… button to allow this Web App to access the new scope you’ve added.
What we have done here is effectively told our web app: “You can access this API we exposed earlier”. Normally, you wouldn’t grant admin consent on behalf of the users (bad manners) but that breaks our little scenario here, so we’re cheating a little bit.
Postman
How let’s set up Postman. Just to clarify, we’re going as far as you need to get a bearer token, we’re not actually connecting to an API yet, although you could. Just make sure it’s configured with the demo API app registration created earlier.
- Click new collection, give it a name.
- Go to the authorization tab, and select OAUTH 2.0 as the type.
- Scroll down and start filling out the details in the Configure new Token section:
- token name: accessToken (can be anything, however you will use this name to use the token when accessing an API)
- grant type: authorization code (with PKCE)
- Callback Uri: https://localhost (make sure it matches what you specified before)
- Auth URL: I am assuming Azure B2C is configured with at least a user signup - signin flow. If you go to any of the app registrations, you’ll see a button called “endpoints”. Copy the authorization end point which looks a bit like this: https://something.b2clogin.com/something.onmicrosoft.com/policy-name/oauth2/v2.0/authorize. Replace the “policy name” portion with the name of your signup-signin flow so it’ll look a bit like: https://something.b2clogin.com/something.onmicrosoft.com/B2C_1_siup/oauth2/v2.0/authorize. Obviously, replace ‘something’ with whatever it is you are using.
- Token URL: same as the Auth URL, Except, replace authorize at the end with token. https://something.b2clogin.com/something.onmicrosoft.com/policy-name/oauth2/v2.0/token
- client ID: this is the APP ID of the ‘demo WEB’ app. You can find this in the properties of the app registration.
- secret: within the app registration you created for Postman, create a secret and paste it here. (if you use the implicit flow, you don’t need to provide a secret)
- scope: This is a of the scopes you are requesting a token for. Start with ‘openid’ (to allow for authentication) and the actual scope that you created in the API app registration. Remember I told you to copy this (by clicking on the copy button). So this field will look something like: openid https://something.onmicrosoft.com/c2704b66-db7d-4330-bb20-c2f5cfa80257/API.access Here we specifying first openid followed by the scope. You can specify multiple values separated by a space. If you want to request a refresh token, add offline_access after openid in this list.
Click Request token. And if all goes well, you will have to login and you should receive a token. Note, you do not actually have to build an API or a web APP to do this. Click Use Token. The token is stored in a variable called accessToken (this is the name you previously specified) and you can send it to an API as part of a request by including it in an authorization header.
Yes, you will have to login as the user you are using for testing. That’s kinda of the point.
Now, if you want to use this token, when creating a request to the API in postman, you will see the option to use authorization and if the request is in the same collection it should pick up the existing token.
The JWT token is sent in a header called “authorization” with a value of ‘bearer <token>’. If it looks strange that you actually see ‘bearer’ followed by a space before the token, remember that the standard for authorization headers is much older than JWTs and ‘bearer’ isn’t the only option. For example, using basic authentication (the kind that pops up a simple username password window in a browser), the credential is also sent in an authorization header. The authorization header then looks like “Basic <Base64encoded username:password>”. This is horribly insecure and really shouldn’t be used any more.
If you want to view the token, copy and paste it to https://jwt.ms
It’s at this point you can now send a request to an API. So, indeed, this is a 2-step process where you first have to get a token (which is quick) and then send it. The token by default has a validity of 1h, this can be changed in the policies in B2C.
Some further learning…
Now, just for educational purposes, let’s change a couple of things in Postman. This is purely if you want to increase your knowledge of OAUTH and OIDC.
Now, just for testing, change the scope to just ‘openid’. Watch what happens…
- We are getting a response, and it contains an id_token, but no access token. The id_token will contain info you can see in jwt.ms, but it won’t work to access an API.
Change the scope to ‘openid offline_access’. Test again.
- The response again contains an id_token, but now also contains a refresh token. A refresh token is used by mobile apps to avoid users having to login over and over again. The refresh token is valid for months and is used to request the bearer token. Through refresh tokens, new bearer tokens are requested in the background. This is important to maintain a good user experience. By default, refresh tokens are also renewed when a new bearer token is requested allowing for an app to remain connected for a very long time.
There’s still no bearer token however, and the reason is simple. So far we’ve authenticated and asked for access to Azure AD or Azure B2C hosted resources (the Graph API), if we want to connect to an API and be able by authorized successfully by that API, we also need to specify that in our request.
So, change the scope to ‘openid offline_access scope" (replace ‘scope’ with the value we previously copied from the API app registration). This time, we get a bearer token that will be accepted for that API.
It’s useful to look at the Postman console log to get a better idea as to what is actually happening… You will see the different requests and responses. This is one of the easiest ways to have visiblity into the actual OIDC and OAUTH authentication messages being sent and received.