So, you've done creating your Web APIs and you wanted to test them and not sure how to start? Read on, as we'll explore how we can easily configure Postman to test protected Web API endpoints. Bear in mind that we won't be creating a Web API project on this post. This post will highlight the various ways on how to configure Postman with Authorization header and ultimately automating the process.

The Scenario

Most Web APIs (if not all) are protected with JSON Web Tokens (JWT). In ASP.NET Core, this is done by configuring our Web APIs with a "Bearer" authentication scheme. When our APIs are decorated with the [Authorize] attribute, the requesting clients should provide the access token generated from the Authorization Server and pass it as a Bearer Authorization Header before clients can be granted access to our API endpoints.  

A client could be a Browser, Web App, Mobile App, Desktop App, another Web API or daemon/service that runs on the background and consumes your APIs - basically, any type of applications and services that support Http protocol.

Authorization Server or sometimes referred to as "Token Server" is the service issuing access tokens to the client after successfully authenticating the resource owner and obtaining authorization. This could be your own custom hosted Auth Server, an Azure B2C,  AWS Cognito, IdentityServer4, OAuth0, Okta, you name it.

Testing Web APIs requires a client. But as a developer, we don't want to create a client application just to test out our APIs. Instead, we can take advantage of some available platforms that will act as the client proxy to test out Web APIs - Postman is one of them.

Sure, you can use Swagger or any other platforms to test your Web APIs, but I personally prefer Postman because I'm comfortable with it and it offers more advance features that you can't do with Swagger.  In this post, we're going to look at how to use Postman for making a simple basic API request, explore the various ways to configure generating tokens, and ultimately automating them. So without further ado, let's jump right into it.

Learning the Basic

If you've been using Postman to peform basic Web API testing, feel free to skip this part. But if haven't tried using Postman before and would like to know how to use it to test your Web APIs, then keep reading as I'll show you how we can easily setup the configuration for you to be able to test protected Web APIs.

Postman is really a handy tool to test API’s without having you to create a UI and it’s absolutely free. In fact, it offers a ton of features that makes it a power tool for managing and testing APIs. If you haven't installed it yet, go ahead and download it here.

After downloading, install it in your machine so you can start testing. The following screenshot shows the Postman app running on my machine using v8.0.6 - the latest version as of this time of writing.

To make a new Http request like GET, POST, PUT, PATCH or DELETE, simply click the + symbol as highlighted from the preceding screenshot. After that, you should be presented with the following display:

In the preceding screenshot, I highlighted the two basic fields to provide. First is the Http Verbs dropdown. From there, you can select what action to perform. By default it defaults to GET. The second field is the API end point that you want to test against.  

Try it in action, and make a GET requrest to Bored API. The following shows the response after clicking the "Send" button:

You see, how we can easily interact with the API. In the preceding screenshot, we got a response back that is shown in the Body tab represented in a JSON format. This works because the Bored API is public and doesn't require any key or access token (JWT). To test protected APIs, you would typically need to acquire an access token first. This means that you must obtain the necessary credentials for you to be able to aqcuire an access token.

Accessing APIs with Client Credentials Flow

Client Credentials Flow is a one of the grant types in OAuth 2.0 in which client applications use client_id, client_secret and sometimes a scope in exchange for an access_token to access a protected API resource.

This flow is the recommended way to secure APIs easily without a particular user connected, mostly this approach is better in server-to-server scenarios, when interconnected internal applications within a system need to authenticate without Login UI to present form logins with username and password.

In Postman, you typically need to perform a POST request along with the following x-www-form-urlencoded parameters:

  • client_id
  • client_secret
  • grant_type
  • scope

The following is the sample ressponse after requesting an access token from the Authorization Server in Postman:

In the preceding screenshot, I'm using IdentityServer4 that acts as the Token Server for generating tokens and running it locally on my machine. In this example, the Issuer or Authority Url is https://localhost:44354/connect/token. Keep in mind that the Url may vary depending on your Identity provider. This should be provided to you along with the client_id, client_secret and scope.  The value of grant_type should always be "client_credentials" for Client Credentials flow.

For more information about IdentityServer4, checkout my previous article about Building a Simple Token Server and Protecting Your ASP.NET Core APIs with JWT

The access_token property from the JSON response in the preceding screenshot is what we care about. This is the JWT value that we need to append to the request header everytime we access the protected API resource. For example, let's assume that we want to access the following GET API endpoint that is protected by your Identity provider.

https://localhost:44380/weatherforecast

Performing just a simple GET request in Postman without the Authorization Header will result to 401 Unauthorized HttpStatus as shown in the following:

To resolved that, we can configure the Authorization key as the header and set the value to bearer <_insert_the_access_token_here>. The following screenshot is the example on how to configure it in Postman:

As you can see, after configuring the bearer token as the Authorization header, the data is now returned for /weatherforecastrequest with status 200 Ok.

Easy right? Now, here's the downside for this setup. Imagine you have lots of different API endpoints with different actions to tests. With this setup, you may end up setting the Authorization Header and set the bearer token everytime you test each API endpoints. This could cost you a development time and could slow down your productivity.

Various Ways on Configuring Bearer Token Generation

The previous approach is is perfectly fine if you are only testing a couple of API endpoints, but when dealing with many endpoints, you should consider automating them as much as possible to improve your productivy.  Let's see how we can do this in Postman.

Please know that there are many ways on how to setup your automation and this is just one of them.

The first approach is using the a Global Variable feature of Postman. Go ahead and click the "eye" icon as shown in the following:

Alternatively, you can click the "Environments" icon from the left panel:

Clicking whichever icon should display the following dialog:

The preceding screenshot allows us to set global or environment-specific variables.  If you are dealing with multiple environments like (Dev, Test, Stage and Prod), then you may want to consider using the Environment feature to setup environment-specific variables. In this example, we're just going to use a a global variable to store the bearer token for the sake of simplicity.

Tip: For more information about variables usage and their scopes, see: Understanding Postman Variables

Now,  click "Edit" and from there, you should be able to define whatever variables you need. For this example, we're going to store the value of access_token value in a variable called AuthTokenVar as shown in the following:

After saving the configuration, the variable AuthTokenVar should be accessible anywhere in your workspace regardless of which environment you're on. Here's the updated configuration of the  /weatherforecastrequest using the AuthTokenVar variable.

As you can see from the preceding screenshot, we've used the Authorization tab instead of manually defining the Authorization key in the request header. This is just to show you a better way to set the Authorization header as you don't have to manually type the word "Bearer" before the access_token or JWT. In this particular approach, we've set the Bearer Token as the type and reference the AuthTokenVar variable to populate the Token TextBox. Postman uses the {{}} syntax to replace variable names enclosed in double curly braces. In this case, the {{AuthTokenVar}} value will be populated with the actual token value.

Sweet! Now, with this approach, you can easily reuse the AuthTokenVar variable when testing different endpoints without having you to manually set the bearer token value all over again. While this works better compared to the previous approach, this still require a manual process to update the AuthTokenVar variable with the access_token value everytime the token expires. Meaning, you invoke a request again to the Authorization Server, get the new token and paste it to the AuthTokenVar variable to update the value. That's a lot of steps!

Can We Improve It?

Yes. But first, let's create a few collection variables to store the authorization credentials we need. The trick here is to group all your API requests into a collection. To do this, click the "Collections" menu and then click the + symbol as shown in the following:

You can name the collection to whatever you like for as long as it's meaningful. 😉 For this example, we're just going to name it as "Weather APIs". Under the "Authorization" tab of the collection, select OAuth 2. 0 as the type just like in the following:

Clicking the OAuth 2.0 item should present you a new screen. Leave the default values as is for now. We'll get back to them later.

Next, let's configure a few local collection variables. Go ahead and switch to the Variables tab and add the following entries:

In the preceding screenshot, we've set the issuer, client_id, client_secret and scope values in it's own variable. Just make sure you replace these values with the correct values you have and then click "Save". Note that we're using local variables here because we wanted to restrict the access within the "Weather APIs" collection only.

Tip: Depending on your needs or if you prefer, you can also define them in Global or Environment variables.

Now, that we have our variables configured locally for the collection use, let's configure OAuth 2.0 for generating tokens. Switch back to the Authorization tab and scroll down to the bottom section where you find the "Configure New Token" section:

In the preceding screeshot, we've set the Token Name, Grant Type and other fields based from the collection variables that we've defined earlier. Once all the values are set, click the "Get New Access Token" button and you should be presented with the following window when the request is successful:

and then displays the response with the access_token as shown in the following:

Finally, click the "Use Token" button to populate the Access Token for the collection and then click "Save" to reflect the configuration changes to the collection.

At this point, whenever you add a new request within the "Weather APIs" collection, all request will be populated automatically with the bearer tokens. Let's see that in action.

Now, click the Add a request link to create a new request or simply right-click on the collection folder and select "Add Request". For this example, we're going to create a GET request to fetch the list of weather forecast just like in our previous example:

Notice that under the Authorization tab, the type is automatically set to "Inherit auth from parent". In this case, the parent is the "Weather APIs" collection folder. You'll also see a message that says:

This request is using OAuth 2.0 from collection Weather APIs.

Which means that there's no need for us to configure any Authorization header for the request and can simply click the "Send" button directy. The following screenhot shows the sample result with this approach:

Great! Please know that the preceding approach still has it's downside as you will still be required to click the "Get New Access Token" button from the collection's Autorization again whenever the token expires. Though it doesn't require you to copy and paste the new token for each of your API requests, it's still quite annoying and could be a burden especially if you are testing a long running test case scenarios in Postman.

Is There a Way to Fully Automate This?

You bet! They key to full automation is writing a script. Fortunately, Postman has a feature that allows us to write scripts using JavaScript to perform custom actions before a request is sent. This is called "Pre-request Script".

Before we begin writing the scripts for automation, let's add the following new collection variables:

The preceding screenshot shows the following newly added variables:

  • WeatherApi_Token_CreatedAt - Holds the token generation date.
  • WeatherApi_Token_ExpiresIn - Holds the token expiry expressed in milliseconds.
  • WeatherApi_Jwt - Holds the value of access_token.

We'll leave the variable values empty as we will be populating them dynamically from the script that we are going to create next.

Creating the Pre-request Script

Now, switch to the "Pre-request Script" tab in the collection and copy the following scripts:

var tokenCreatedAt = pm.collectionVariables.get("WeatherApi_Token_CreatedAt");

if (!tokenCreatedAt) {
    tokenCreatedAt = new Date(new Date().setDate(new Date().getDate() - 1))
}

var tokenExpiresIn = pm.collectionVariables.get("WeatherApi_Token_ExpiresIn");

if (!tokenExpiresIn) {
    tokenExpiresIn = 5000;
}

var tokenCreatedTime = (new Date() - Date.parse(tokenCreatedAt))

if (tokenCreatedTime >= tokenExpiresIn) {

    console.log("The token has expired. Attempting to request a new token.");

    pm.sendRequest({
        url: pm.variables.get("WeatherApi_AuthorityUrl"),
        method: 'POST',
        header: {
            'Accept': 'application/json',
            'Content-Type': 'application/x-www-form-urlencoded'
        },
        body: {
            mode: 'urlencoded',
            urlencoded: [{
                    key: "client_id",
                    value: pm.collectionVariables.get("WeatherApi_ClientId"),
                    disabled: false
                },
                {
                    key: "client_secret",
                    value: pm.collectionVariables.get("WeatherApi_ClientSecret"),
                    disabled: false
                },
                {
                    key: "scope",
                    value: pm.collectionVariables.get("WeatherApi_Scope"),
                    disabled: false
                },
                {
                    key: "grant_type",
                    value: "client_credentials",
                    disabled: false
                }
            ]
        }
    }, function(error, response) {
        console.log(response.json().access_token);
        
        pm.collectionVariables.set("WeatherApi_Token_CreatedAt", new Date());
        pm.collectionVariables.set("WeatherApi_Jwt", response.json().access_token);

        var expiresIn = response.json().expires_in;
        
        if (expiresIn) {
            tokenExpiresIn = expiresIn * 1000;
        }
        
        pm.collectionVariables.set("WeatherApi_Token_ExpiresIn", tokenExpiresIn);
    });
}

Let's see what we just did by breaking out the code.

The first thing that we did there was getting the value of WeatherApi_Token_CreatedAt variable. When running the script for the first time, that call will return an empty value because we haven't set any value for that variable. Let's take a look at the next code:

if(!tokenCreatedAt){
  tokenCreatedAt = new Date(new Date().setDate(new Date().getDate()-1)) 
}

The preceding code initializes the tokenCreatedAt variable with the previous day. This is to ensure that we issue a request for getting a new access token on the first run.  Same goes for the handling the tokenExpiresIn value:

if(!tokenExpiresIn){
    tokenExpiresIn = 5000; 
}

The preceding code initializes the tokenExpiresIn value to 5 seconds when the WeatherApi_Token_ExpiresIn is empty.

Now that we have initialized both tokenCreatedAt and tokenExpiresIn with default values. We can then do a validation check to compare their values as shown in the following code:

var tokenCreatedTime = (new Date() -  Date.parse(tokenCreatedAt))

if(tokenCreatedTime >= tokenExpiresIn) {
   //removed other code for brevity
}

The preceding code converts the tokenCreatedAt in milliseconds format so we can compare it against the tokenExpiresIn. Obviously, on the first run this will result to true because the tokenCreatedAt expressed in milliseconds will always be greather than 5 seconds. Again, this was the intent so we can send a request to aquire a new access_token when the script is running for the first time.

Within the if-condition statement, we invoked the sendRequest() function as shown in the following:

pm.sendRequest({
     // remove code for brevity
  },function (error, response) { 
     // remove code for brevity
  });
}

The sendRequest() function is responsible for invoking an Http request to the Authorization Server. It basically takes 2 arguments: 1 for the request and 2 for handling the response. Let's take a look at the request first:

url: pm.variables.get("WeatherApi_AuthorityUrl"),
    method: 'POST',
    header: {
        'Accept': 'application/json',
        'Content-Type': 'application/x-www-form-urlencoded'
    },
    body: {
        mode: 'urlencoded',
        urlencoded: [{
                key: "client_id",
                value: pm.collectionVariables.get("WeatherApi_ClientId"),
                disabled: false
            },
            {
                key: "client_secret",
                value: pm.collectionVariables.get("WeatherApi_ClientSecret"),
                disabled: false
            },
            {
                key: "scope",
                value: pm.collectionVariables.get("WeatherApi_Scope"),
                disabled: false
            },
            {
                key: "grant_type",
                value: "client_credentials",
                disabled: false
            }
        ]
    }

The preceding code makes an Http POST request to the Authorization Server. So instead of us manually invoking this call in Postman, this script autmates that process. You notice that all urlencoded parameters are extracted from the variables that we defined in the "Weather APIs" collection. This makes our script very easy to manage whenever each of those values are changed.

The second argument to the sendRequest() is for handing the response based on the Http request call as shown in the following code:

function(error, response) {
    console.log(response.json().access_token);
    
    pm.collectionVariables.set("WeatherApi_Token_CreatedAt", new Date());
    pm.collectionVariables.set("WeatherApi_Jwt", response.json().access_token);

    console.log(pm.collectionVariables.get("WeatherApi_Token_CreatedAt"));

    var expiresIn = response.json().expires_in;

    if (expiresIn) {
        tokenExpiresIn = expiresIn * 1000;
    }
    
    pm.collectionVariables.set("WeatherApi_Token_ExpiresIn", tokenExpiresIn);
}

The preceding code is the crucial part of the script because this is where the magic happens. What the code does will extract the values from the JSON response and set the corresponding values for the following variables:

  • WeatherApi_Token_CreatedAt - stores the current date.
  • WeatherApi_Token_ExpiresIn - stores the expires_in value from the token response if available. Typically this will have a default value of 3600 or 1 hour. We need to mutiple this value by 1000 to get the milliseconds.
  • WeatherApi_Jwt - stores the access_token returned from the token response.

That's it! Just a reminder, don't forget to "Save" your script before moving out to a different tab.

Updating the Authorization Configuration

The final step that we need to do is to update our Authorization configuration. Go ahead and switch to the Authorization tab and replace the "Access Token" value with the {{WeatherApi_Jwt}} variable just like in the following screenshot:

At this point, we can now remove all the configurations within the "Configure New Token" section because we no longer them for this approach. This approach now automates everything, no manual clicks required, no need for copy pasting and every request will authenticate automatically.

Now, hit the "Save" button and start making a request to your APIs. After testing your APIs, you should notice that the new variables that we defined are automatically populated just like in the following screenshot:

Sweet! Thank you for reading and I hope you find this post helpful!