# Understanding the OAuth Flow for Microsoft Entra OBO(On Behalf Of) Agent Identities - Part 1

My previous [article](https://www.azureguru.net/understanding-the-oauth-flow-for-microsoft-entra-autonomous-agent-identities) on OAuth Flow for autonomous agent identities focused on generating authentication tokens for agent identities to act autonomously without user context through client credentials flows.

This approach works well when you want an agent to run with autonomy but in scenarios where you want to implement an OBO(On Behalf Of) OAuth flows the implementation is quite extensive and requires multiple settings and clear and concise understanding of the underlying flow.

This article is Part 1 of the two part series on OBO flow for agent identities.

In this article we will focus on setting up and implementing the prerequisites required for a Microsoft Entra Agent Identity OBO (On-Behalf-Of) authentication flow.

We will walk through creation and configuration of the essential artifacts, that includes Agent Identity OAUTH2 scope permissions, application registrations, API permissions and federated identity credentials.

We will also determine if we require agent identity blue print inheritable Permissions configured in a specific use cases.

In Part 2 of the series , we will cover the implementation of the authentication flow using Azure Function and a console application as a client that invokes this Azure function.

> **Our final aim of the process should be a token that can access downstream resource (Azure Storage) through delegated identity and permissions of the signed-in user.**

**Note** : Throughout this article I have extensively used Microsoft Graph API Explorer to perform and execute the configurations and API requests instead of PowerShell scripts.

### **Authentication flow Diagram**

![](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/d0e08ba6-e752-478f-8a8e-54b3024b9001.png align="center")

For the flow, we first require a user token (**Tc**) created followed by a request for the **TUAMI** (User Managed Identity Token)which is then followed by the token **T1** (Exchange Token) and finally the **OBO** token (On Behalf Of Token) is generated for the user.

> **Azure Storage will be our final resource for which we acquire the final OBO access token.**

When we obtain the final OBO token, its claims should have the following

- **aud**: Azure Storage
- **scp**: should contain `user_impersonation`
- **oid**: should identify the signed-in user (user principal id)

**Agent Identity and Blue Print :**

I have an existing Agent Identity named : **Agent Identity** that I am going to use

![](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/54646ecb-4f6a-419d-9da4-152d25a63da9.png align="center")

and an agent Blue Print named : **Blue Print**

![](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/06e7f6a9-c3a5-479d-bc4e-924bd1762053.png align="center")

### Federated Identity Credentials (FIC) and Agent Blue Print

I have a managed identity named : **Federated_Credentials_Function_Identity**

![](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/97918105-cfac-4c04-bde2-0fa41c56f7c4.png align="center")

We will use this Managed Identity to create a Federated Identity Credential (FIC) for the agent blueprint .

In Microsoft Entra admin center, under your agent blueprint navigate to Credentials(Preview) option

![](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/99b6824d-9330-4e44-8fcd-d9a26b06036f.png align="center")

and select Managed Identity option under **Federated credential scenario** and then select the user managed identity that you want to use. Keep the rest of the options in the set up to default.

![Agent autonomous app OAuth flow](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/32a6e661-96ff-405c-80d6-3e2c6b22e0e5.png align="center")

I selected the managed identity **Federated_Credentials_Function_Identity**

### **Entra Application Registration**

Register a new application in Microsoft Entra ID. I created a new one with name **Agent Identity App**

![](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/481325e8-7692-4e77-ad70-98e29f3983ee.png align="center")

We will use the client id of this app in our client code for generating an user token **Tc** with OAuth2 scope `api://{BluePrint App ID}/access_agent`

## Setting up the identifier URI and OAuth2 scope

Our agent will receive incoming requests from users, so it needs an identifier URI to identify the blueprint as a resource and OAuth2 scope.

Navigate to Microsoft Graph Explorer : <https://developer.microsoft.com/en-us/graph/graph-explorer>

We will execute a **PATCH** request that configures the "Expose an API" functionality for the agent identity blueprint.

```python
PATCH https://graph.microsoft.com/v1.0/applications/{BluePrint App ID}
```

Request Body

```json
{
  "identifierUris": [
    "api://{BlurPrint App ID}"
  ],
  "api": {
    "oauth2PermissionScopes": [
      {
        "adminConsentDescription": "Allow the application to access the agent on behalf of the signed-in user.",
        "adminConsentDisplayName": "Access agent",
        "id": {Any random custom GUID value}
        "isEnabled": true,
        "userConsentDescription": "Allow the application to access the agent.",
        "userConsentDisplayName": "Access agent",
        "type": "User",
        "value": "access_agent"
      }
    ]
  }
```

> The scope name used in this example is : **access_agent.** You can use any other name that suits your implementation

![](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/fe1490c3-3ad0-4145-8195-666ef7a75ad0.png align="center")

Post execution, you can cross verify if the permission scope was successfully configured by executing the following GET request.

```python
https://graph.microsoft.com/v1.0/servicePrincipals?$filter=appId eq '{BluePrint App ID}'&$select=id,appId,displayName,oauth2PermissionScopes	                                                                                            
```

![](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/4d70a48f-4fba-485f-9a28-85a4789d7fd9.png align="center")

We see in the above output that the value property is now set for the scope **access_agent**.This confirms that the oauth2PermissionScopes are now correctly configured.

> If the PATCH request fails with error message : User doesn't have sufficient privileges to perform this action, ensure that you have the scope access for **AgentIdentityBlueprint.UpdateAuthProperties.All**

This can be verified by the following **GET** request

```python
https://graph.microsoft.com/v1.0/servicePrincipals?$filter=appId eq '00000003-0000-0000-c000-000000000000'&$select=id,appId,oauth2PermissionScopes
```

where `00000003-0000-0000-c000-000000000000` is app ID for Microsoft Graph.

Search for the scope text **AgentIdentityBlueprint.UpdateAuthProperties.All** in the output from the above execution.

![](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/40c7ec49-013f-494a-830e-90d08650dea2.png align="center")

If its not found, then use the following POST request to set it up

```python
https://graph.microsoft.com/v1.0/oauth2PermissionGrants
```

Request Body

```json
{
  "clientId": "{Graph explorer Object ID}", 
  "consentType": "AllPrincipals",
  "resourceId": "{Agent identity Object ID}",
  "scope": "AgentIdentityBlueprint.UpdateAuthProperties.All"
}
```

where the client ID is the **Graph explorer Object ID.**

The **Graph explorer Object ID** value can be found in Entra admin center under Enterprise Applications >> All applications

![](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/67c3a70b-1865-4e29-8244-7554f26602ac.png align="center")

Once the POST request is executed, you should get the 201 success message.

![](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/cc80b807-40ac-420d-8471-70464b29dfdd.png align="center")

Verify the scope permission by clicking **Graph Explorer** in Microsoft Entra under Enterprise apps>>Security>> Permissions and you should see the **AgentIdentityBlueprint.UpdateAuthProperties.All** claim value in the list.

![](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/9c31ddf6-75b7-4316-9a1e-d40e54f8e69d.png align="center")

After the permission access (if any) is resolved, re-execute the following PATCH request with the request body provided earlier

```python
PATCH https://graph.microsoft.com/v1.0/applications/{BluePrint App ID}
```

and this time the PATCH request to set the identifier URI and OAuth2 scope should succeed.

### **Configuring** `user_impersonation` **delegated Scope for Azure Storage**

This step is required to authorize the Agent Identity to access Azure Storage on behalf of the signed-in user.

When an agent performs an OBO (On-Behalf-Of) flow it needs permission to access the downstream resource using the user's delegated identity. In our example the downstream resource is Azure Storage and the delegated permission is `user_impersonation`.

We first have to find the user object principal id for whom the delegated permission is to be granted.

The user in this example is **sachin.nandanwar @** **azureguru.net**

```python
https://graph.microsoft.com/v1.0/users?$filter=userPrincipalName eq 'sachin.nandanwar@azureguru.net'&$select=id,displayName
```

![](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/4bbe1ae2-e20c-4819-9511-8f3a1846f487.png align="center")

Since our downstream API is Azure storage, we have to find the client id for Azure storage .

Select any one of the App registrations in the Microsoft Entra admin center and select API permissions and in the pop up that follows navigate to the **API's my organization uses** and search for **Azure Storage**.

![](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/d8731a82-ca92-407a-aabc-8f5f8a0d08eb.png align="center")

Now that we have the client id of the downstream resource, we have to figure out the **Principal Id** for the Azure Storage. We can do that by executing the following GET request

```python
https://graph.microsoft.com/v1.0/servicePrincipals?$filter=appId eq 'e406a681-f3d4-42a8-90b6-c2b029497af1'&$select=id,appId,displayName					                                                                                            
```

![](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/6bdfa9a7-ebaf-4ec0-82b5-b4cf70a31406.png align="center")

We now have the user principal id and the downstream resource principal id (Azure storage). We can go ahead and assign `user_impersonation` scope for the signed in user.

Execute the following **POST** request

```python
https://graph.microsoft.com/v1.0/oauth2PermissionGrants
```

with the following request body

```json
{
   "ClientID"    : "Agent Identity ID",
   "consentType" : "Principal",
   "principalId" : "User principal ID" ,
    "resourceId" : "Downstream resource principal ID",
    "scope"      : "user_impersonation"
}
```

![](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/08b949c5-f688-427b-959f-7e59208b7b3a.png align="center")

As the permission pre-existed, re executing the same request results in a **Conflict 409** error. If the execution is for the first time, the output will be a **Created 201** success message.

This permission can be cross verified by the following GET request

```python
https://graph.microsoft.com/v1.0/users?$filter=userPrincipalName eq 'sachin.nandanwar@azureguru.net'&$select=id,displayName,oauth2PermissionScopes
```

Replace `sachin.nandanwar@azureguru.net` in the above request with your intended user.

![](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/2de9304f-cec5-4ea1-85e2-433fd0bf7495.png align="center")

> **Very Important : Ensure that the user identity is assigned the necessary RBAC role for Azure storage**

### **Do we require Inheritable permissions in this example ?**

The short answer is **NO**

I was under an assumption that it is required but given that we have already granted the required delegated scope for the downstream resource (Azure storage) for user principal , the agent identity will act on behalf of the signed in user to access the downstream resource.

Blueprint's permissions determines which downstream permissions the Agent Identity is allowed to inherit. But as we have directly assigned the downstream access to the user there is no need to implement blueprint inheritable permissions.

> The Blueprint defines what the agent is allowed to access through inheritable permissions, the user's identity establishes on whose behalf the blueprint accesses the downstream resource through `user_impersonation` delegated scopes and Azure RBAC determines what operations that user can perform on the downstream resource.

But in scenarios when there are multiple agents and users , its impractical to assign the downstream resource access to every individual user. In such scenarios you grant `user_impersonation` scope to a single blue print and all the underlying agents inherit this scope and acts on behalf of the user provided that the `aud` in the claim of the user token is validated against the OAUTH2 scope of the agent blue print.

I think I should have a separate article on this topic.

### Takeaways

In this article, we configured the OAuth 2.0 scopes and redirect URIs and set up the `user_impersonation` delegated permission for Azure Storage.

We also explored how the Agent Identity uses the On-Behalf-Of (OBO) flow to access downstream resources in our case Azure Storage for the signed-in user.

In the next article, we will dive into the code implementation of the complete token acquisition flow, walk through each token exchange step by step and validate the final access token issued for the downstream resource.

Till then.. Keep learning !!!!
