Pages

Wednesday, November 15, 2023

Securing Azure OpenAI with API Management to only allow access for specified Azure AD users

I’ve been spending most of my weekends playing around with Azure’s OpenAI service and two of the personal projects I’ve been working on are:

  1. How can I secure access to OpenAI’s API access so control can be applied to what and who can make API calls to it
  2. How can I capture identity details for the application or user making the API call if we are to secure access with OAuth

This post will focus on item #1 while I get the notes I’ve captured for #2 organized and written as a blog post.

A common method I’ve found to provide the type of security for #1 is through leveraging the API Management service so I gave this pattern a shot over the weekend to test using an Azure API Management to only allow specified Azure AD users to call the Azure OpenAI API. The following is a high level architecture diagram and the flow of the traffic:

     image

Setup Azure API Management to publish Azure OpenAI

Begin by downloading the latest Azure OpenAI inference.json from the following Microsoft documentation: https://learn.microsoft.com/en-us/azure/ai-services/openai/reference#completions

image

For the purpose of this example, I will use the latest 2023-09-01-preview: https://github.com/Azure/azure-rest-api-specs/blob/main/specification/cognitiveservices/data-plane/AzureOpenAI/inference/preview/2023-09-01-preview/inference.json

image

Once downloaded, open the JSON file and edit the following two lines:

Use the name of the OpenAI instance to replace {endpoint}:

"url": "dev-openai/openai",

Use the full endpoint value:

"default": https://dev-openai.openai.azure.com/

image

image

With the JSON file prepared, proceed to deploy an Azure API Management resource with the SKU of choice, select the APIs blade, Add API and select OpenAPI:

image

Select Full and import the inference.json file that will automatically populate the fields, proceed to create the API:

image

Turn on the System Assigned Managed for the APIM:

image

We’ll need to allow the APIM to call Azure OpenAI with the API key:

image

… and the best way to store the key is through a KeyVault so I’ve created a secret with the API key in a KeyVault:

image

As well as granted the APIM managed system identity Key Vault Secrets User permissions to access the key:

image

With the KeyVault and OpenAI secret configured proceed to navigate to the APIM Named values blade and Add a new value:

image

Configure a named value to reference the secret in the KeyVault:

image

Note the name that you’ve used for the named value as you’ll be using it later on.

We’ll also be using the tenant ID for another configuration so repeat the same procedure and create a plain value with the tenant ID:

image

The following named values should be listed:

image

Proceed by navigating to the APIs blade, Azure OpenAI Service API, All operations, Design tab, and then click on the </> icon under the Inbound processing heading:

image

We’ll be configuring the following policy for the APIM to send a header with the name api-key and value of the secret we configured in the KeyVault:

GitHub repository: https://github.com/terenceluk/Azure/blob/main/API%20Management/XML/Set-Header-API-Key.xml

<!--

    IMPORTANT:

    - Policy elements can appear only within the <inbound>, <outbound>, <backend> section elements.

    - To apply a policy to the incoming request (before it is forwarded to the backend service), place a corresponding policy element within the <inbound> section element.

    - To apply a policy to the outgoing response (before it is sent back to the caller), place a corresponding policy element within the <outbound> section element.

    - To add a policy, place the cursor at the desired insertion point and select a policy from the sidebar.

    - To remove a policy, delete the corresponding policy statement from the policy document.

    - Position the <base> element within a section element to inherit all policies from the corresponding section element in the enclosing scope.

    - Remove the <base> element to prevent inheriting policies from the corresponding section element in the enclosing scope.

    - Policies are applied in the order of their appearance, from the top down.

    - Comments within policy elements are not supported and may disappear. Place your comments between policy elements or at a higher level scope.

-->

<policies>

<inbound>

<base />

<set-header name="api-key" exists-action="append">

<value>{{dev-openai}}</value>

</set-header>

</inbound>

<backend>

<base />

</backend>

<outbound>

<base />

</outbound>

<on-error>

<base />

</on-error>

</policies>

**Note that we use the {{ }} brackets reference the named value as a variable.

image

Proceed to save the settings.

The APIM is now set up for receiving OpenAI API calls but not with the Azure OpenAI api-key, but rather a subscription key for the APIM instance. To retrieve this key, navigate to the APIs blade, Azure OpenAI Service API, Settings tab, and then scroll down to the Subscription heading. Notice that Subscription required is enabled with the Header name and Query parameter name defined. The subscription key can be found in the Subscriptions blade:

image

API Management Logging Configuration

One last configuration that is important is the Application Insights:

image

… and Azure Monitor logging:

image

Ensure that these are enabled so APIM data plane access logs and reports can be created. A few sample reports generated with KQL can be found here: https://github.com/Azure-Samples/openai-python-enterprise-logging

Here are a few sample outputs from 2 KQL queries:

Query to identify token usage by ip and mode

ApiManagementGatewayLogs

| where tolower(OperationId) in ('completions_create','chatcompletions_create')

| where ResponseCode == '200'

| extend modelkey = substring(parse_json(BackendResponseBody)['model'], 0, indexof(parse_json(BackendResponseBody)['model'], '-', 0, -1, 2))

| extend model = tostring(parse_json(BackendResponseBody)['model'])

| extend prompttokens = parse_json(parse_json(BackendResponseBody)['usage'])['prompt_tokens']

| extend completiontokens = parse_json(parse_json(BackendResponseBody)['usage'])['completion_tokens']

| extend totaltokens = parse_json(parse_json(BackendResponseBody)['usage'])['total_tokens']

| extend ip = CallerIpAddress

| where model != ''

| summarize

sum(todecimal(prompttokens)),

sum(todecimal(completiontokens)),

sum(todecimal(totaltokens)),

avg(todecimal(totaltokens))

by ip, model

image

GitHub repository: https://github.com/terenceluk/Azure/blob/main/Kusto%20KQL/Identify-token-usage-by-ip-and-mode.kusto

Query to monitor prompt completions

ApiManagementGatewayLogs

| where tolower(OperationId) in ('completions_create','chatcompletions_create')

| where ResponseCode == '200'

| extend model = tostring(parse_json(BackendResponseBody)['model'])

| extend prompttokens = parse_json(parse_json(BackendResponseBody)['usage'])['prompt_tokens']

| extend prompttext = substring(parse_json(parse_json(BackendResponseBody)['choices'])[0], 0, 100)

image

GitHub repository: https://github.com/terenceluk/Azure/blob/main/Kusto%20KQL/Monitor-prompt-completions.kusto

If you have experience setting API Management up to capture requests to Azure OpenAI then you will already know that the only information representing the calling user the Log Analytics provide is the IP address. This isn’t very useful so I have written another post to demonstrate how to capture the OAuth token details used to make the call:

How to log the identity of a user using an Azure OpenAI service with API Management logging (Part 1 of 2)
https://terenceluk.blogspot.com/2023/11/how-to-log-identity-of-user-using-azure.html

Testing OpenAI API calls through API Management with Postman

With the API Management configuration completed, we should now be able to use Postman to test querying the APIM. I won’t go into the details of the configuration but will provide the screenshots:

https://dev-openai-apim.azure-api.net/deployments/{{gpt_mode_4}}/chat/completions?api-version={{api_env_latest}}

image

{

"messages": [

   {

"role": "user",

"content": "how many faces does a dice have?"

    }

  ],

"temperature": 0.7,

"top_p": 0.95,

"frequency_penalty": 0,

"presence_penalty": 0,

"max_tokens": 800,

"stop": null

}

image

I’ll write another post in the future to properly secure Azure OpenAI now that we APIM publishing the APIs.

Create an App Registration for securing APIM API access

With the Azure API Management configured to publish the Azure OpenAI APIs, we will now proceed to create an App Registration that will allow us to lockdown APIM access for select Entra ID / Azure AD users.

image

Provide a name for the App Registration and create the object:

image

Select the App roles blade, click on Create app role and fill out the following:

Display name: <Provide a display name>

Allowed member types: Select Users/Groups or Both (Users/Groups + Applications)

Value: APIM.Access

Description: Allow Azure OpenAI API access.

Create the app role.

imageimage

Select the Expose an API blade, and click on the Add link beside Application ID URI:

image

Leave the Application ID URI as the default and click on the Save button:

image

We’ll be using Azure CLI to quickly test the retrieval of the token so we’ll need to create a scope and add Azure CLI as an authorized client application.

Proceed to click on Add a scope and fill in the following properties:

Scope name: API.Access

Who can consent: Admins and users

Admin consent display name: Access to Azure OpenAI API

Admin consent description: Allows users to access the Azure OpenAI API

State: Enabled

image

Click on Add a client application to add the Client ID of Azure CLI 04b07795-8ddb-461a-bbee-02f9e1bf7b46 as an authorized application to retrieve a delegated access token:

image

I will also be demonstrating how to set up Postman to test the retrieval of the token so we’ll need to add the Redirect URI for the call back to Postman for the App Registration by navigating to the Authentication blade, click on Add a platform, and add the following URI: https://oauth.pstmn.io/v1/callback

image

We will also need to create a secret for the App Registration so Postman is able to securely authenticate and retrieve a delegated token on behalf of the user. Navigate to the Certificates & secrets blade, create a Client secret then save the secret: 

image

With the App Registration created, we’ll need to grant a user with the role to test calling the APIM’s OpenAI publish API. Copy the client ID of the App Registration, navigate to the Enterprise Application blade and search for the Applicaiton ID:

image

Open the Enterprise Application object, navigate to the Users and groups blade, and click on Add user/group:

image

Select the user who we’ll be testing with and assign the user:

imageimage

With the Enterprise Application configured with the user assigned, we will now proceed to lockdown the APIM inbound processing policy. Open the APIM resource in the portal, navigate to the APIs blade, Azure OpenAI Service API, Design tab, and click on the </> button under Inbound processing:

image

Proceed to add the <vadlidate-jwt> tag content and note that we use the {{Tenant-ID}} named value variable we created earlier:

GitHub Repository: https://github.com/terenceluk/Azure/blob/main/API%20Management/XML/Validate-JWT-Access-Claim.xml

<!--

    IMPORTANT:

    - Policy elements can appear only within the <inbound>, <outbound>, <backend> section elements.

    - To apply a policy to the incoming request (before it is forwarded to the backend service), place a corresponding policy element within the <inbound> section element.

    - To apply a policy to the outgoing response (before it is sent back to the caller), place a corresponding policy element within the <outbound> section element.

    - To add a policy, place the cursor at the desired insertion point and select a policy from the sidebar.

    - To remove a policy, delete the corresponding policy statement from the policy document.

    - Position the <base> element within a section element to inherit all policies from the corresponding section element in the enclosing scope.

    - Remove the <base> element to prevent inheriting policies from the corresponding section element in the enclosing scope.

    - Policies are applied in the order of their appearance, from the top down.

    - Comments within policy elements are not supported and may disappear. Place your comments between policy elements or at a higher level scope.

-->

<policies>

<inbound>

<base />

<set-header name="api-key" exists-action="append">

<value>{{bma-dev-openai}}</value>

</set-header>

<validate-jwt header-name="Authorization" failed-validation-httpcode="403" failed-validation-error-message="Forbidden">

<openid-config url=https://login.microsoftonline.com/{{Tenant-ID}}/v2.0/.well-known/openid-configuration />

<issuers>

<issuer>https://sts.windows.net/{{Tenant-ID}}/</issuer>

</issuers>

<required-claims>

<claim name="roles" match="any">

<value>APIM.Access</value>

</claim>

</required-claims>

</validate-jwt>

</inbound>

<backend>

<base />

</backend>

<outbound>

<base />

</outbound>

<on-error>

<base />

</on-error>

</policies>

image

Proceed to save and we are now ready to test with Azure CLI.

Testing Token Retrieval with Azure CLI and API Management API calls with Postman

Launch a prompt with Azure CLI available and execute:

Az login

Complete the login with the test account:

image

Next, we’ll need to copy the Application ID URI:

image

… and execute:

az account get-access-token --resource api://12bccc26-b778-4a2d-ae7a-4f5732e7a79d

image

A token should be returned:

image

Copying the token and pasting it into https://jwt.io/ should confirm that the token has the role APIM.Access:

image

You should now be able use the token to call APIM with delegated access with a 200 OK status:

image

Trying to call APIM without a token passed in the header as Authorization will fail with:

{

"statusCode": 403,

"message": "Forbidden"

}

image

Removing the user from the Enterprise Application and attempting to call APIM will also result in the same failure message:

image

{

"statusCode": 403,

"message": "Forbidden"

}

image

Testing Token Retrieval and API Management API calls with Postman 

Proceed to launch Postman, navigate to the Environments are and create the following variables.

tenant_id: <The App Registration’s Directory (tenant) ID>

client_id_APIM: <The App Registration’s Application (client) ID>

client_secret_APIM: <The secret we created earlier>

Next, create a new request, navigate to the Authorization tab and fill in the following:

Type: OAuth 2.0

Add authorization data to: Request Headers

Token: Available Tokens

Header Prefix: Bearer

Token Name: <Name of preference>

Grant type: Authorization Code

Callback URL: https://oauth.pstmn.io/v1/callback

Authorize using browser: Enabled

Auth URL: https://login.microsoftonline.com/{{tenant_id}}/oauth2/v2.0/authorize

Access Token URL: https://login.microsoftonline.com/{{tenant_id}}/oauth2/v2.0/token

Client ID: {{client_id_APIM}}

Client Secret: {{client_secret_APIM}}

Scope: api://12bccc26-b778-4a2d-ae7a-4f5732e7a79d/API.Access

Client Authentication: Send as Basic Auth header

**Note the default Callback URL is set as https://oauth.pstmn.io/v1/callback, which is the URL we configured earlier for the App Registration’s Redirect URI.

Leave the rest as default and click on Get New Access Token:

imageimage

A window with Get new access token prompt will be displayed with a browser directing you to the login.microsoftonline.com to log into Entra. Proceed to log into Entra ID to retrieve the token.

Repeat the steps for Postman as demonstrated in the Azure CLI instructions to call the OpenAI endpoints through the APIM management with the token.

----------------------------------------------------------------------------------------------------------------------------

I hope this helps anyone who may be looking for a way to lock down APIM access when publishing Azure OpenAI APIs. There are other infrastructure components that will need to be secured to ensure no calls can reach the Azure OpenAI API and I will write another blog post for the design and configuration in the future.

Monday, November 13, 2023

Configuring Postman to use OAuth 2.0 for application and delegated permissions in Entra ID / Azure AD

Entra ID / Azure AD authentication and authorization are important components for service access within any environment and one of the common questions I’ve been asked is what is the difference between application and delegated permissions in the context of App Registration permissions. The short answer is that application permissions will have the application authenticate as itself, while delegated is where the application will authenticate on behalf of a user. The following Microsoft document provides a great walkthrough of the differences between the two: https://learn.microsoft.com/en-us/graph/auth/auth-concepts. These two types of authentication and authorization can have impactful security consequences when designing applications so it is important to use the correct model. What I’ve found to be most effective in explaining this is demonstrate how to perform both authentication with Postman so the purpose of this post will be a demonstration of setting up the authentication to retrieve the Bearer token, and then calling an API using the token with the appropriate authorization.

One point I would like to highlight is that Postman provides a built-in OAuth 2.0 provider authentication tab that makes the process of retrieving the JWT token much simpler than manually configuring an API GET call to the endpoint but this in turn means you won’t know the details of what is sent to the API endpoint. With that, I will show both methods as there are benefits of knowing exactly what header and body content are being sent if you’re writing an application or script to retrieve a token.

Setting up an App Registration in Entra ID

Before we set up Postman, we will need to create an App Registration to represent the Postman so it can be used to authenticate against Entra ID / Azure AD and use its authorized permissions to perform activities such as use the Graph API.

Begin by setting up an App Registration in Entra ID:

image

Provide a name for the App Registration, adjust the supported account types for either single tenant or multitenant, leave the Redirect URI as blank for now, and create the instance:

image

With the App Registration created, we can now copy the property values that we’ll use within Postman to authenticate against Entra ID / Azure AD. Navigate to the Overview blade and copy the following values:

  • Application (client) ID
  • Directory (tenant) ID
image

Proceed to create a secret for the App Registration and make sure you copy the value of the secret before navigating away as you cannot retrieve the secret after leaving the page:

image

Permissions need to be configured for this App Registration to allow our Postman to call the Graph API either on behalf of the user using Postman, or as an application identity configured for Postman. Proceed to navigate to the API Permissions blade and note the default User.Read permission already granted to the App Registration.

Let’s proceed to grant 2 additional permissions to the App Registration for testing both Application and Delegated permissions by clicking on Add a permissions:

image

Click on Microsoft Graph:

image

Note that this is where you configure Delegated permissions or Application permissions for the application using this App Registration to authenticate against Entra ID / Azure AD:

image

We’re going to configure both for the purpose of this walkthrough so select Delegated permissions, search for User.Read, select User.Read.All and then add the permission:

image

Repeat the process for Application permissions:

image

Some permissions will need consent granted by an admin and the 2 that were configured require this so click on Grant admin consent for <Company Name> to grant the consent:

image

A green check box will be displayed once consent has been granted:

image

The last step is to configure the Redirect URI we left as blank during the creation of the App Registration. Navigate to the Authentication blade and look for the following field:

Web

Redirect URIs

The URIs we will accept as destinations when returning authentication responses (tokens) after successfully authenticating or signing out users. The redirect URI you send in the request to the login server should match one listed here. Also referred to as reply URLs. Learn more about Redirect URIs and their restrictions

The purpose of the redirect URI, or reply URL, is for Entra ID to know where to send the authentication response once the App Registration has been successfully authorized and granted an authorization code / access token. In the case of this example, we are sending the code back to Postman and the URL to use is:

https://oauth.pstmn.io/v1/callback

image

For more information about the Redirect URI, see the following documentation: https://learn.microsoft.com/en-us/entra/identity-platform/reply-url#redirect-uris-in-application-vs-service-principal-objects

The configuration for the App Registration representing Postman is now complete.

Setting up Postman

Having a nicely organized setup of Postman collections and variables will save you time during testing and improve the handling of sensitive values. Start by creating a Collection for this exercise.

image

Next, navigate to the Environments menu to configure the following secret and variables that were saved earlier during the App Registration creation that we be using for our API calls.

  • client_id
  • client_secret
  • tenant_id
image

Application Permissions

Let’s start with the simpler application permissions configuration.

Using Postman’s Authorization Feature to get Token

Begin by clicking on the ellipsis icon and select Add request:

image

Then navigate to the Authorization tab and fill in the following:

Type: OAuth 2.0

Add authorization data to: Request Headers

Token: Available Tokens

Header Prefix: Bearer

Token Name: <Name of preference>

Grant type: Client Credentials

Access Token URL: https://login.microsoftonline.com/{{tenant_id}}/oauth2/v2.0/token

Client ID: {{client_id}}

Client Secret: {{client_secret}}

Scope: https://graph.microsoft.com/.default

Client Authentication: Send as Basic Auth header

Leave the rest as default and click on Get New Access Token:

imageimage

The operation should return the information as shown in the screenshots below:

imageimage

Proceed to copy the Access Token value and with this, we can navigate to https://jwt.io/, paste the value to inspect the details of the token and confirm that the App Registration ID and permissions are displayed for this token:

image

Next, we can use this token to perform a Graph API call and confirm that we are allowed retrieve the information. Create a new request and configure the following parameters:

GET https://graph.microsoft.com/v1.0/users

Under Headers, fill in the following Key and Value:

Authorization: <Paste the token created in the previous step>

Content-Type: application/json

Proceed to click on the Send button:

image

The list of users in Entra ID should be displayed:

image

As this is an application authentication, there are certain Graph APIs that it would unable to call and an example of this is the /ME where the following error would be thrown:

{

"error": {

"code": "BadRequest",

"message": "/me request is only valid with delegated authentication flow.",

"innerError": {

"date": "2023-11-11T16:03:48",

"request-id": "8840028e-9a93-4e89-9818-fbcd77e19b60",

"client-request-id": "8840028e-xxxx-xxxx-xxxx-fbcd77e19b60"

        }

    }

}

image

I’ll show how we can make this call this with delegated permissions later in this post.

Directly calling API Endpoint to get Token

Retrieving an OAuth token via the Postman Authorization feature was easy but there are scenarios where we want to call the API endpoint instead because we will be using the same header and body settings in an application. The following are the header and body configuration for Postman.

Begin by clicking on the ellipsis icon and select Add request, then navigate to the Headers tab and fill in the following:

GET https://login.microsoftonline.com/{{tenant_id}}/oauth2/v2.0/token

Content-Type: application/x-www-form-urlencoded

image

Navigate to the Body tab, select form-data and fill in the following:

grant_type: client_credentials

client_id: {{client_id}}

client_secret: {{client_secret}}

Scope: https://graph.microsoft.com/.default

Client on the Send button to submit the request and confirm that the access token is returned:

image

Pasting the token into https://jwt.io/ will return the same results as the previous Postman Authorization request:

image

Repeat the steps above to call the GET https://graph.microsoft.com/v1.0/users API endpoint will return the same results.

Delegated Permissions

Let’s continue moving onto delegated permissions where the App Registration will call the Graph API on behalf of the user.

Using Postman’s Authorization Feature

Begin by clicking on the ellipsis icon and select Add request:

image

Then navigate to the Authorization tab and fill in the following:

Type: OAuth 2.0

Add authorization data to: Request Headers

Token: Available Tokens

Header Prefix: Bearer

Token Name: <Name of preference>

Grant type: Authorization Code

Callback URL: https://oauth.pstmn.io/v1/callback

Authorize using browser: Enabled

Auth URL: https://login.microsoftonline.com/{{tenant_id}}/oauth2/v2.0/authorize

Access Token URL: https://login.microsoftonline.com/{{tenant_id}}/oauth2/v2.0/token

Client ID: {{client_id}}

Client Secret: {{client_secret}}

Scope: https://graph.microsoft.com/.default

Client Authentication: Send as Basic Auth header

**Note the default Callback URL is set as https://oauth.pstmn.io/v1/callback, which is the URL we configured earlier for the App Registration’s Redirect URI.

Leave the rest as default and click on Get New Access Token:

imageimage

The following Get new access token prompt will be displayed with a browser directing you to the login.microsoftonline.com to log into Entra. Proceed to log into Entra ID to retrieve the token:

image

The following will be displayed upon successfully authenticated. Note the URL displayed in the browser:

https://oauth.pstmn.io/v1/callback?code=0.AVEAC0f0hB4_iUiflasPUUMCT1d_QBUsqGVPhgiMkNV16uVRANQ.AgABAAIAAAAmoFfGtYxvRrNriQdPKIZ-AgDs_wUA9P-CoVXQzw9BTb3FWH1dw6QqHEhjwSMkqJy1t5HGBarSFJY2Ckt4VaBvEF8-wBc3iirPRCA-Eo44cVWUBspus3IsPj521nlt9KqQbNjs0Ub8QMZJbRuocDMoHEyG3v6VGxH-MM_ll5NX34ys5wwTt8v01wjx0ZhAv9I-hUovaikvHFAup5BA19MJNaNmrNZS7tD9PEfw_F-Vs84tGaVJ0NXTfSLZIZnSmlVg1nsnk9-94P3W2x_mzdIwX3eho4p2yFiHK803pZrJKN7BaQnrgK5RfYL1mdYKMUCE7dfH0w1rQsYgREVYK6_KMOEYCzX_Z8D8vMJQl2Nd3xG9zCQPdVY-tcVxmUXNf3R29OlRvAqq3w-ZlGuwTCxIFdvX0ljOt5xWpzVo5oAqR7UQKjwIo-F2aVkJ4KcTIdjbg8TWcX7lOsjBuR8ZkdGIYBVtRTzrWskElP-Dxx6NP_LpDI4VInu7j4Uxs4ebIxMAF9YsGoS0M6vvY6cSvoFZcG_PXJYYk2I4tgqDS7FrEQ9ihk26x86--YRiwEO26tExgsxee0IfJVhuIqFNMqc-3FmGalsXnZxeDzYpppJpZarmAmnr2GjXVELWJhjB5bjt-Wsg9aCHAx_eb8rAKrxveOWEqGMLp9cBp5M&session_state=378b4cdc-e06b-4624-9618-047c3f12d5fc#

… and the value of code= in the URL. While this is not important for using Postman’s Authorization tab for retrieving the token, this is the code that is passed back to Postman to generate a token for the user who has just logged in:

image

The token will be displayed:

imageimage

Copying the token and pasting it into https://jwt.io/ will return details and we can see that this token is issued in the content of the user who has just authenticated. The user details are provided and because this account is synchronized from an on-premise Active Directory, the onprem_sid is also provided:

image

This token can then be used to call the Get Users API endpoint as well as the /ME endpoint because the App Registration is now calling on behalf of a user and not itself:

image

Directly calling API endpoint to get Token

Using the Postman’s Authorization Feature is very easy but manually calling the endpoint requires a few extra steps. As shown in the previous steps, delegated permissions will require the user to interactively log in to retrieve a code that will then be used by the application to retrieve a token for the user. There isn’t a way for us to automatically launch a browser when sending a request to the API endpoint but we can manually retrieve it by using a browser.

To obtain the code for a user identity we’ll need to launch browser and navigate to a URL with specific parameters. The components we’ll need are:

Tenant ID: <The tenant ID of Azure AD>

App Registration Client ID: <The client ID of the App Registration>

Redirect URI: <This is where you will use the code generated to retrieve a token for the user>

Scope: <The scope in which we’ll be using this token for>

Here is how the browser URL string should be configured:

https://login.microsoftonline.com/<tenantID>/oauth2/v2.0/authorize?client_id=<appRegistrationClienID>&response_type=code&redirect_uri=<URLwhereTheTokenIsReturned>&response_mode=query&scope=<scope>

Here is a sample of what the browser URL string would look like:

https://login.microsoftonline.com/84f4470b-3f1e-xxxx-xxxx-ab0fxxxx024f/oauth2/v2.0/authorize?client_id=154xxxx7-xxxx-4f65-xxxx-8c90d575eae5&response_type=code&redirect_uri=https://oauth.pstmn.io/v1/callback&response_mode=query&scope=https://graph.microsoft.com/.default

Customize the tenant ID and client ID, and use the following for:

Redirect URI: https://oauth.pstmn.io/v1/callback

Scope: https://graph.microsoft.com/.default

Paste the URL into the browser, authenticate into Entra ID and a string similar to the following will be displayed in the browser navigation bar upon successfully authenticating as the user:

https://oauth.pstmn.io/v1/callback?code=0.AVEAC0f0hB4_iUiflasPUUMCT1d_QBUsqGVPhgiMkNV16uVRAOA.AgABAAIAAAAmoFfGtYxvRrNriQdPKIZ-AgDs_wUA9P-gz3Qp7LOLK9NV2bmKKD5T1KzlW2EwwHmIyq-a0AXhwSpil3quUTpV_dW7ruHNnenPskMN4hOoTaMY0lkTzRoMM58Ta95ayQXvNUt-yPttTUkkpYTISCrcSo29LRMX2RcKdx7opKABxxSAOwQAp1D9eIkJlTltkDjS70yv86r1Agl7MDawQx8YYcXUj1tP8wevrvFMcKPJQNPhq83YepJOypSB0nl3EYq7mPZmyjV9BWh3IMnV4t4qbfa4y14iQyMpkSuGLSudZh2JNNfZxirT56Y8B9una2oBjFHMMBvDIdSY-caXUJe1qytIXUs1jFeljMhtv9gKuBiWMMdk8aPUt-7twIpgpBjktdIJ8ihsK1MdGy0jBz0zd96eG8bSJTRGgXkQv87dsX0O761XSP_tqd8EqzthkDGR5iixvuZcBuQeaIs4YJ_tb9pVtWLSufrbmka1Rqz0ghbZiFk1TA1bqKr-S3_LJsoiUaCCKdYH_lohvfRBlzdtHXLhxx8y8iGddw82Mlj-Bzk_gc_N-Vq7YLa71CL1r__aJ89AfM_klt_ouGy_vHx_9BxPc3VRXTrjsuB9tDoqIlb9UA91JMe9oRySMQASCErucmFVZ_G7JJGYUchpNsuu4HP0kzHakuGGLTBJGHRV8gRowj63X4B4QEARL-4Khqo&session_state=c78c420a-d68a-4713-bcf5-e04910dc3902

image

The code we need for our Postman call is in between code= and &session_state as highlighted above.

Navigate to the Body tab, select form-data and fill in the following:

grant_type: authorization_code

code: <paste the code retrieved from the browser URL after authentication>

client_id: {{client_id}}

client_secret: {{client_secret}}

redirect_uri: https://oauth.pstmn.io/v1/callback

Client on the Send button to submit the request and confirm that the access token is returned:

image

The token retrieval should complete:

image

With the token successfully retrieved, we can navigate to the JWT Token portal: https://jwt.io/ and paste the token into the text field to confirm that the expected User (not App Registration) and roles are configured:

imageimage

With the this token successfully retrieved, we can repeat the steps from the previous examples to perform a Graph API calls.

I hope this provides a bit more clarity on the differences between application and delegated permissions as well as how to set up Postman to perform both types of authentication and authorization.