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. Start the flow by calling the /authorize endpoint
  2. Capture the state parameter from the /u/login endpoint redirection
  3. Make a POST request to the /u/login endpoint:
    • Use the state parameter in both Request Body and Request Params
    • Pass the username and password in the body (x-www-form-urlencoded)
    • Capture the new state from the Location header
  4. Make a GET request to the /authorize/resume endpoint using the state from the previous step
  5. Retrieve the authorization code
  6. Exchange the code for a token by sending a POST request to the /oauth/token endpoint

Postman collection

To simplify the process, use the following collection. Save it 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: Before running the collection, ensure that the following environment variables are set in Postman:

  • auth0_domain: Your Auth0 domain
  • auth0_client_id: Your Auth0 client ID
  • redirect_uri: The authorized redirect URI for your application

To use this collection:

  1. Save the JSON as “AuthCodeFlow.json”
  2. Open Postman and navigate to File > Import
  3. Choose the “AuthCodeFlow.json” file
  4. Set up the required environment variables
  5. Run the collection to test the Authentication Code Flow

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}}