Testing Authorization Code Flow Using Postman

Overview

This article explains how to test the Authorization Code Flow with Postman.

Applies To

  • Postman
  • Authorization Code Flow

Solution

Follow the steps below to test the Authorization Code Flow using Postman:

  1. Call the /authorize endpoint to initiate the authorization code flow
  2. From the redirection to the /u/login endpoint, capture the state parameter
  3. Make a POST request to the /u/login endpoint
  4. In the request, use the state parameter captured in step #2 in both the Request Body and Request Parameters
  5. In the x-www-form-urlencoded body of the request, include the username and password
  6. From the response Location header, capture the new state parameter
  7. Make a GET request to the /authorize/resume endpoint, using the state parameter captured in step #4
  8. Retrieve the authorization code from the response
  9. Exchange the authorization code for a token by sending a POST request to the /oauth/token endpoint

Use the Postman collection provided below to simplify the process.

Before running the Postman collection, ensure the following environment variables are configured in Postman:

  • auth0_domain: <The Auth0 domain, e.g., tenant-name.auth0.com>
  • auth0_client_id: <The Client ID of the Auth0 application>
  • redirect_uri: <The Redirect URI configured in the Auth0 application, e.g., http://localhost:3000/callback>

To use the Postman collection for testing the Authorization Code Flow:

  1. Save the below provided JSON definition as AuthCodeFlow.json.
  2. Open Postman and navigate to File > Import.
  3. Select the AuthCodeFlow.json file to import the collection.
  4. Ensure the environment variables listed above are correctly set in the active Postman environment.
  5. Run the collection.

NOTE: Performing the interactive portion of the Authorization Code Flow (i.e., calls to the /authorize endpoint) directly within Postman can be unreliable. The Postman collection is designed to handle a direct username and password submission compatible with the New Universal Login experience, but may fail if the authentication flow encounters variations such as:

  • Classic Universal Login is used.
  • Identifier First login flow is enabled.
  • Multi-Factor Authentication (MFA) is triggered. (MFA stands for Multi-Factor Authentication and should be defined if this is its first use in the article).
  • A consent screen is presented.
  • A session cookie is present, causing the login prompt to be skipped.
  • An Action script within Auth0 redirects the flow.
  • Other changes or customizations to the interactive login process.

A more resilient method for testing involves obtaining the authorization code through a standard browser and then using Postman to exchange this code for tokens. This approach accommodates any authentication prompt or variation presented to the user.

To obtain the authorization code using a browser and then exchange it in Postman:

  1. Ensure the <redirect_uri> (as configured in the environment variables and listed in the Auth0 application’s Allowed Callback URLs) is effectively inactive for this manual test. For example, if the <redirect_uri> is http://localhost:3000/callback, ensure no local application is running and listening on that address. An active listener could automatically intercept and use the authorization code.
  2. Open the browser’s Developer Tools and select the Network tab.
  3. In the browser’s address bar, manually construct and navigate to the /authorize endpoint URL. Replace the placeholders with the actual values:
https://<auth0_domain>/authorize?response_type=code&client_id=<auth0_client_id>&redirect_uri=<redirect_uri>
  1. Complete the authentication and authorization steps as prompted in the browser (e.g., login, consent).
  2. After successful authorization, the browser will attempt to redirect to the specified <redirect_uri>. This redirect may result in a browser error page if no service is listening at that address, which is expected for this manual method. In the Developer Tools Network tab, find the request made to this <redirect_uri>.
  3. From the full URL of this redirect request (often visible in the “Headers” section for the request in Developer Tools), extract the value of the code query parameter. This is the authorization code.
  4. In Postman, create a new request. Make a POST request to the token endpoint: https://<auth0_domain>/oauth/token.
  5. Include the following parameters in the body of this POST request, typically formatted as x-www-form-urlencoded:
  • grant_type: authorization_code
  • client_id: <auth0_client_id>
  • code: <authorization_code_obtained_in_step_6>
  • redirect_uri: <redirect_uri> ``

Save the following collection as “AuthCodeFlow.json” and import it into Postman:

{
	"info": {
		"_postman_id": "0bea1c8c-313e-4156-985d-74c11539f428",
		"name": "Auth Code Flow",
		"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
		"_exporter_id": "15882021"
	},
	"item": [
		{
			"name": "1. Get to /authorize",
			"event": [
				{
					"listen": "test",
					"script": {
						"exec": [
							"pm.test(\"GetState\", function () {",
							"    console.log(`Retrieving STATE...`)",
							"    let state = pm.response.headers.get('Location');",
							"    state = state.slice(state.lastIndexOf('=') + 1);",
							"    pm.environment.set(\"state\", state);",
							"    console.log(`Setting STATE (${state}) as environmental variable.`);",
							"    postman.setNextRequest(\"2. Post to /u/login\");",
							"});"
						],
						"type": "text/javascript"
					}
				}
			],
			"protocolProfileBehavior": {
				"followRedirects": false
			},
			"request": {
				"method": "GET",
				"header": [],
				"url": {
					"raw": "https://{{auth0_domain}}/authorize?response_type=code&client_id={{auth0_client_id}}&redirect_uri={{redirect_uri}}",
					"protocol": "https",
					"host": [
						"{{auth0_domain}}"
					],
					"path": [
						"authorize"
					],
					"query": [
						{
							"key": "response_type",
							"value": "code"
						},
						{
							"key": "client_id",
							"value": "{{auth0_client_id}}"
						},
						{
							"key": "redirect_uri",
							"value": "{{redirect_uri}}"
						}
					]
				}
			},
			"response": []
		},
		{
			"name": "2. Post to /u/login",
			"event": [
				{
					"listen": "test",
					"script": {
						"exec": [
							"pm.test(\"GetResumeState\", function () {",
							"    console.log(`Retrieving RESUME_STATE...`)",
							"    let resume_state = pm.response.headers.get('Location');",
							"    resume_state = resume_state.slice(resume_state.lastIndexOf('=') + 1);",
							"    pm.environment.set(\"resume_state\", resume_state);",
							"});"
						],
						"type": "text/javascript"
					}
				}
			],
			"protocolProfileBehavior": {
				"followRedirects": false
			},
			"request": {
				"method": "POST",
				"header": [],
				"body": {
					"mode": "urlencoded",
					"urlencoded": [
						{
							"key": "username",
							"value": "b@b.com",
							"type": "text"
						},
						{
							"key": "password",
							"value": "1234",
							"type": "text"
						},
						{
							"key": "state",
							"value": "{{state}}",
							"type": "text"
						}
					]
				},
				"url": {
					"raw": "https://{{auth0_domain}}/u/login?state={{state}}",
					"protocol": "https",
					"host": [
						"{{auth0_domain}}"
					],
					"path": [
						"u",
						"login"
					],
					"query": [
						{
							"key": "state",
							"value": "{{state}}"
						}
					]
				}
			},
			"response": []
		},
		{
			"name": "3. Get to /resume",
			"event": [
				{
					"listen": "test",
					"script": {
						"exec": [
							"pm.test(\"GetCode\", function () {",
							"    console.log(`Retrieving CODE...`)",
							"    let code = pm.response.headers.get('Location');",
							"    code = code.slice(code.lastIndexOf('=') + 1);",
							"    pm.environment.set(\"code\", code);",
							"});"
						],
						"type": "text/javascript"
					}
				}
			],
			"protocolProfileBehavior": {
				"followRedirects": false
			},
			"request": {
				"method": "GET",
				"header": [],
				"url": {
					"raw": "https://{{auth0_domain}}/authorize/resume?state={{resume_state}}",
					"protocol": "https",
					"host": [
						"{{auth0_domain}}"
					],
					"path": [
						"authorize",
						"resume"
					],
					"query": [
						{
							"key": "state",
							"value": "{{resume_state}}"
						}
					]
				}
			},
			"response": []
		},
		{
			"name": "4. Exchange CODE against a TOKEN",
			"event": [
				{
					"listen": "test",
					"script": {
						"exec": [
							"pm.test(\"GetToken\", function () {",
							"    console.log(`Retrieving TOKENS...`)",
							"    let tokens = pm.response.json();",
							"    console.log(tokens);",
							"});"
						],
						"type": "text/javascript"
					}
				}
			],
			"request": {
				"method": "POST",
				"header": [],
				"body": {
					"mode": "urlencoded",
					"urlencoded": [
						{
							"key": "grant_type",
							"value": "authorization_code",
							"type": "text"
						},
						{
							"key": "client_id",
							"value": "{{auth0_client_id}}",
							"type": "text"
						},
						{
							"key": "code",
							"value": "{{code}}",
							"type": "text"
						},
						{
							"key": "redirect_uri",
							"value": "{{redirect_uri}}",
							"type": "text"
						},
						{
							"key": "audience",
							"value": "",
							"type": "text",
							"disabled": true
						}
					]
				},
				"url": {
					"raw": "https://{{auth0_domain}}/oauth/token",
					"protocol": "https",
					"host": [
						"{{auth0_domain}}"
					],
					"path": [
						"oauth",
						"token"
					]
				},
				"description": "This is the OAuth 2.0 grant that regular web apps utilize in order to access an API. Use this endpoint to exchange an Authorization Code for an Access Token."
			},
			"response": []
		}
	]
}

Note that doing the interactive portion of the flow (i.e. /authorize) with Postman will be very fragile. The code above is prepared to handle a username/password prompt in the New Universal Login, but it will fail if something is not configured as expected, such as:

  • Classic Universal Login is configured for the login screen
  • Identifier First is enabled
  • MFA is enabled
  • A consent screen is displayed
  • Any other future change in the interactive login flow
  • A session cookie is provided so the login prompt is skipped
  • An action performs a redirect

As such, a more resilient flow would be to obtain the authorization code in the browser and then use it in Postman to exchange the code for the token results. This involves more manual work, but works with any authorization prompt presented to the user.

  • Pick an inactive {{redirect_uri}} for the app (otherwise, the app will do the code exchange). E.g. http://localhost:3000/callback, and make sure the app is not running.

  • With the Developer Tools open in the Network tab, navigate manually to the /authorize endpoint (https://{{auth0_domain}}/authorize?response_type=code&client_id={{auth0_client_id}}&redirect_uri={{redirect_uri}}) in the browser (replace the placeholders with the corresponding values). Complete all the authorization steps.

  • Look for the request to the {{redirect_uri}} endpoint. Grab the code from the URL.

  • Use the code, along with the other parameters, in a POST request to https://{auth0_domain}/oauth/token providing the following values in the body:

    • grant_type=authorization_code
    • client_id={{auth0_client_id}}
    • code={{the_resulting_code}}
    • redirect_uri={{redirect_uri}}