# Understanding the OAuth Flow for Autonomous Agent Identities

Microsoft strongly recommends against using client secrets as client credentials in production environment for AI Agent Identity blueprints.

![](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/78f44cbd-d496-4494-a938-b073b85b5737.png align="left")

And there is a good reason for this recommendation. Client secrets could potentially be leaked ,exposed in source code or configuration files or end up being logged in application logs.

Microsoft recommends more secure authentication mechanisms, such as federated identity credentials with managed identities or client certificates wherever supported, which improves the overall security of AI Agents running in the Microsoft ecosystem.

In this post I am going to delve into all the nitty-gritty details of setting up autonomous agent app OAuth flow through federated integrated credentials.

> Please note that this article aims to explore the detailed setup of an autonomous agent app's OAuth flow without any requirement of user interaction or user context. It does not specifically deals with OBO (On Behalf Off) flow. I will have separate article that deep dives into that aspect.

You can refer to my earlier article on Agent identities and blue prints [here](https://www.azureguru.net/agent-identity-for-ai-agents-in-microsoft-agent-framework)

I am not going to use any PowerShell scripts that makes everything look so confusing. Performing the same set up through UI is more easy and relatable to understand.

### Authentication flow Diagram

![](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/f5a14422-58c6-49ca-a820-2c647f8b945d.png align="center")

To understand the authentication flow , we first request the TUAMI(User Managed Identity Token) followed by token T1 (Exchange Token) and finally followed by the TR token (Resource Access Token).

### Sequence Diagram

![](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/916187b6-7da0-4633-9fce-e801687be0af.png align="center")

*Image Credit : Microsoft*

### Key Considerations

- Resource in this example is Azure storage
- The generation of the final token **TR**(Resource Access Token) is through an Azure Function with `aud`: `https://storage.azure.com`
- The Azure function runs on a Managed Identity and obtains **T1** through Federated token Exchange
- The same managed identity is used in setting up the Federated Credentials for the agent blueprint
- The credentials used are Federated Identity Credential (FIC) of an existing Managed Identity
- The blueprint then presents the credentials to Microsoft Entra.
- It returns TUAMI token through [**ManagedIdentityCredential**](https://learn.microsoft.com/en-us/dotnet/api/azure.identity.managedidentitycredential?view=azure-dotnet)
- The agent blueprint requests an exchange token T1 through the TUAMI token.
- `aud` : `api://AzureADTokenExchange` is used for token exchange
- ***T1 is generated through the following code snippet***

```javascript
POST /oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded

client_id=AgentBlueprint id
&scope="api://AzureADTokenExchange/.default"
&fmi_path=AgentIdentity id
&client_assertion_type="urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
&client_assertion=TUAMI
&grant_type="client_credentials"
```

`TUAMI` >> Federated Managed Identity token

`client_id` >> Agent blueprint id

`fmi_path` >> Agent Identity id i.e. the Application id

![Agent autonomous app OAuth flow](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/9909d4b7-bd25-456f-825f-ebb713931cc2.png align="left")

- ***TR is obtained through the following POST request after the generation of the T1 token***

```javascript
 POST /oauth2/v2.0/token
 Content-Type: application/x-www-form-urlencoded 

  ["client_id"] = AgentIdentity id
  &["scope"] = "https://storage.azure.com/.default"
  &["grant_type"] = "client_credentials"          
  &["client_assertion_type"] = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
  &["client_assertion"] = T1
```

> **Note** : The scope used here is `https://storage.azure.com/.default`

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

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

![Agent autonomous app OAuth flow](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/4dd41dbb-78fd-4ddd-b88f-4be0f2ffd1d6.png align="center")

To 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.

![Agent autonomous app OAuth flow](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/74410cb3-27e5-400a-b5d9-d2096d802ad0.png align="center")

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 existing managed identity **Federated_Credentials_Function_Identity**

This concludes our Managed Federated Credentials set up for the agent blue print.

### Azure Function

This is an very important aspect in the entire flow.

Create a new Azure Function and add the following references

```csharp
dotnet add package Azure.Core;
dotnet add package Azure.Identity;
dotnet add package Microsoft.AspNetCore.Http;
dotnet add package Microsoft.AspNetCore.Mvc;
dotnet add package System.Text.Json;
```

Add bunch of variables and set up the constructor for logger

```csharp
string TenantId = "Tenant id";
string UserManagedIdentityId = "Client id of user assigned managed identity";
string AgentIdentityId = "Agent identity id";  
string AgentBluePrintId ="Blue Print id of the agent identity" ;

private readonly ILogger<Function1> _logger;
private static readonly HttpClient client = new HttpClient();
public HttpClient Client => client;

public Function1(ILogger<Function1> logger)
{
    _logger = logger;
}
```

I will walk through the process step by step explaining how to obtain different types of token.

Under the Azure Function **Function1** which is of type **Get**

```csharp
[Function("Function1")]
public async Task<IActionResult> Run(
 [HttpTrigger(AuthorizationLevel.Anonymous, "get")] HttpRequest req)
{

}
```

add the following code

***TUAMI (Token Managed Identity) >*\>**

We will use [ManagedIdentityCredential](https://learn.microsoft.com/en-us/dotnet/api/azure.identity.managedidentitycredential?view=azure-dotnet) to generate the TUAMI token based on the federated credentials through Managed Identity.

```csharp
  var miCredential = new ManagedIdentityCredential(
      ManagedIdentityId.FromUserAssignedClientId(
          UserManagedIdentityId));
  var assertionContext = new TokenRequestContext(
      new[] { "api://AzureADTokenExchange/.default" });

  AccessToken assertionToken;
  try
  {
      assertionToken = await miCredential.GetTokenAsync(
          assertionContext,
          CancellationToken.None);
  }
  catch (Exception ex)
  {
      _logger.LogError(ex, "UAMI assertion token failed");

      return new ObjectResult(new
      {
          stage = "UAMI assertion token acquisition",
          exceptionType = ex.GetType().FullName,
          error = ex.Message,
          details = ex.ToString()
      })
      {
          StatusCode = 500
      };
  }

  string TUAMI = assertionToken.Token;
```

The resulting access token value is stored in the variable **TUAMI .**

***T1 Token* >>**

Earlier in the article I mentioned of using the following request to obtain token **T1**

```javascript
POST /oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded

client_id=AgentBlueprint id
&scope="api://AzureADTokenExchange/.default"
&fmi_path=AgentIdentity id
&client_assertion_type="urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
&client_assertion=TUAMI
&grant_type="client_credentials"
```

Implement the above POST request in C#

```csharp
 var parameters_T1 = new Dictionary<string, string>
 {
      ["client_id"] = AgentBluePrintId,
      ["scope"] = "api://AzureADTokenExchange/.default", 
      ["fmi_path"] = AgentIdentityId,
      ["client_assertion_type"] = "urn:ietf:params:oauth:client-assertion-type:jwt- bearer",
      ["client_assertion"] = TUAMI,
      ["grant_type"] = "client_credentials"
 };

 HttpContent contents = new FormUrlEncodedContent(parameters_T1);

 var HttpMessage_T1 = new HttpRequestMessage
 {
     Method = HttpMethod.Post,
     RequestUri = new Uri($"https://login.microsoftonline.com/{TenantId}/oauth2/v2.0/token"),
     Content = contents
 };

 HttpResponseMessage response_T1 = await client.SendAsync(HttpMessage_T1);

 string T1;

 try
 {
     response_T1.EnsureSuccessStatusCode();

     using JsonDocument doc = JsonDocument.Parse(response_TUAMI.Content.ReadAsStringAsync().Result);

     T1 = doc.RootElement
        .GetProperty("access_token")
        .GetString()!;

 }
 catch (Exception ex)
 {
     _logger.LogError(ex, "Federated token exchange failed");

     return new ObjectResult(new
     {
         stage = "Federated token exchange",
         exceptionType = ex.GetType().FullName,
         error = ex.Message,
         details = ex.ToString()
     })
     {
         StatusCode = 500
     };

 }
```

The resulting access token is stored under variable **T1**.

***TR token >>***

To obtain token TR, we have to use the following POST request

```javascript
 POST /oauth2/v2.0/token
 Content-Type: application/x-www-form-urlencoded 

  ["client_id"] = AgentIdentity id
  &["scope"] = "https://storage.azure.com/.default"
  &["grant_type"] = "client_credentials"          
  &["client_assertion_type"] = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
  &["client_assertion"] = T1
```

Implement the above POST request in C#

```csharp
var parameters_TR = new Dictionary<string, string>
{
    ["client_id"] = AgentIdentityId,
    ["scope"] = "https://storage.azure.com/.default",
    ["grant_type"] = "client_credentials",
    ["client_assertion_type"] = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
    ["client_assertion"] = T1
};

HttpContent contents_TR = new FormUrlEncodedContent(parameters_TR);

var HttpMessage_TR = new HttpRequestMessage
{
    Method = HttpMethod.Post,
    RequestUri = new     Uri($"https://login.microsoftonline.com/{TenantId}/oauth2/v2.0/token"),
    Content = contents_TR
};

try
{
    response_T1.EnsureSuccessStatusCode();
    HttpResponseMessage response_TR = await client.SendAsync(HttpMessage_TR);
    return new OkObjectResult(new
    {
        reponse = response_TR.Content.ReadAsStringAsync().Result,
        stage = "Resource token acquisition",
        message = "Resource token acquisition success"
    });
}
catch (Exception ex)
{
    _logger.LogError(ex, "Resource token acquisition failed");
    return new ObjectResult(new
    {
        stage = "Resource token acquisition",
        exceptionType = ex.GetType().FullName,
        error = ex.Message,
        details = ex.ToString()
    })
    {
        StatusCode = 500
    };
}
```

Publish the Azure Function

![Agent autonomous app OAuth flow](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/2119002b-ecf5-46e1-86a7-9f12a85393f8.png align="center")

The resulting token is returned through the **OkObjectResult** type of the Azure Function.

In next step , add the User Managed Identity to the published Azure Function.

In this example it is **Federated_Credentials_Function_Identity**

![Agent autonomous app OAuth flow](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/328b4bd8-ab6f-438d-9f93-7016ca61ce60.png align="center")

Once all this is set, go ahead and execute the Azure Function in the portal.

![Agent autonomous app OAuth flow](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/b9c7b261-9b55-4704-b67f-5ec2a0f5f37a.png align="center")

On execution ,the function **Function1** should return the TR token in the final output.

Validate the token TR by inspecting its generated JWT token value.

![Agent autonomous app OAuth flow](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/f4788cf8-ea00-45c3-9420-9422decba7c3.gif align="center")

***Verification of the TR token claim values***

| ***Claims*** | ***Values*** |
| --- | --- |
| **aud** | [*https://storage.azure.com*](https://storage.azure.com) |
| **appid/azp** | *Agent Identity AppId* |
| **appidacr** | *2* |
| **xms_par_app_azp** | *Blueprint AppId* |
| **sub** | *Agent Identity OID* |

I tweaked the code of **Function1** so that it returns the **T1** token in the output so we could inspect its claims and values.

Validate the token T1 by inspecting its generated JWT token value.

![Agent autonomous app OAuth flow](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/f6e91462-d288-45ed-8a8f-2010fcb751b7.gif align="center")

***Verification of the T1 token claim values***

| ***Claims*** | ***Values*** |
| --- | --- |
| **aud** | *Token exchange value* |
| **appid/azp** | *Agent Blueprint Id* |
| **appidacr** | *NA* |
| **xms_par_app_azp** | *NA* |
| **sub** | */eid1/c/pub/t/…/a/…/&lt;agentAppId>* |

That's all !!!

Congratulations !!! You have set up Agent autonomous app OAuth flow.

### **Key takeaways**

- Client secrets don’t work for **T1/TR** flows.We either have to use certificates or FIC.
- **T1** (`scope = api://AzureADTokenExchange`) is the exchange token.
- **TR** (`scope = https://storage.azure.com/`) is the resource access token.
- The Azure function runs under the same Managed identity used in the Agent Blueprint Federated credentials set up.
- This article dealt only with app only operations enabling agent identities to act autonomously
- I will post another article on how to use the **OBO**(On Behalf Off) for agent identities.

Till then.. Keep learning !!!
