Skip to content

Native agentic token for UserManagedIdentity via fmi_path exchange#448

Open
Hameedkunkanoor wants to merge 1 commit into
microsoft:mainfrom
Hameedkunkanoor:hameed/msal-umi-agentic-fmi
Open

Native agentic token for UserManagedIdentity via fmi_path exchange#448
Hameedkunkanoor wants to merge 1 commit into
microsoft:mainfrom
Hameedkunkanoor:hameed/msal-umi-agentic-fmi

Conversation

@Hameedkunkanoor

Copy link
Copy Markdown

Summary

Adds native support to MsalAuth.get_agentic_application_token for acquiring an agentic application token when AUTH_TYPE is AuthTypes.user_managed_identity, using the federated fmi_path exchange. Today this path raises RuntimeError, forcing callers to monkey-patch get_agentic_application_token.

Problem

For AuthTypes.user_managed_identity, _create_client_application returns an MSAL ManagedIdentityClient. MSAL's ManagedIdentityClient does not support the federated fmi_path token exchange required for agentic application tokens, so get_agentic_application_token falls through to raise RuntimeError for that auth type.

In the Azure AI Foundry hosted-agent (digital-worker) model, the container is configured with UserManagedIdentity, so consumers currently work around this by replacing the method at runtime:

MsalAuth.get_agentic_application_token = _get_agentic_application_token_via_default_azure_credential

This proposal moves that logic into the SDK so no auth code is required from the developer.

Fix

Adds a branch to get_agentic_application_token: for user_managed_identity it acquires the token via azure-identity's DefaultAzureCredential with identity_config={""fmi_path"": <agent_app_instance_id>} (and managed_identity_client_id from CLIENT_ID when set) against api://AzureAdTokenExchange/.default. azure-identity is imported lazily and a clear ImportError is raised if it is not installed, so it stays an optional dependency.

Relationship to #413

#413 added a distinct AuthTypes.identity_proxy_manager (IDPM) path that does a direct managed-identity acquire_token_for_client(resource=...) call. That does not perform the fmi_path federated exchange the digital-worker model needs, and plain user_managed_identity still raised. This change complements #413 by covering the user_managed_identity + fmi_path case natively.

Testing

  • Updated the ""unsupported client"" test to use system_managed_identity (still correctly raises RuntimeError).
  • Added a user_managed_identity test that injects a fake DefaultAzureCredential via sys.modules (so it runs without azure-identity installed) and asserts the fmi_path identity_config, the managed_identity_client_id, the exchange scope, and credential cleanup.
  • 30/30 tests in test_msal_auth.py pass; black clean.

MsalAuth.get_agentic_application_token raised RuntimeError for
AuthTypes.user_managed_identity because MSAL's ManagedIdentityClient does not
support the federated fmi_path exchange required for agentic application tokens.
Callers had to monkey-patch get_agentic_application_token to work around this.

This adds a native branch that, for user_managed_identity, acquires the token via
azure-identity's DefaultAzureCredential with identity_config={"fmi_path": ...}
(and managed_identity_client_id from CLIENT_ID when set) against the managed-
identity endpoints. azure-identity is imported lazily and a clear ImportError is
raised if it is not installed, so it stays an optional dependency.

- New MsalAuth._acquire_agentic_token_via_managed_identity helper.
- Updated the unsupported-client test to use system_managed_identity (still
  raises); added a user_managed_identity test that injects a fake
  DefaultAzureCredential (no azure-identity needed to run the test).
- black clean (libraries + tests); 30/30 msal_auth tests pass.

Note: complements microsoft#413 (identity_proxy_manager), which added a distinct IDPM
resource path but does not perform the fmi_path federated exchange.
@Hameedkunkanoor Hameedkunkanoor requested a review from a team as a code owner July 8, 2026 09:38
Copilot AI review requested due to automatic review settings July 8, 2026 09:38

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds native support in MsalAuth.get_agentic_application_token for AuthTypes.user_managed_identity to acquire agentic application tokens via a federated fmi_path exchange using azure-identity’s async DefaultAzureCredential, eliminating the need for consumers to monkey-patch SDK behavior.

Changes:

  • Added a user_managed_identity branch in get_agentic_application_token that performs the fmi_path exchange via DefaultAzureCredential against api://AzureAdTokenExchange/.default.
  • Introduced _acquire_agentic_token_via_managed_identity helper with lazy azure-identity import and best-effort credential cleanup.
  • Updated tests to (a) cover the new user_managed_identity path and (b) keep the “unsupported client” RuntimeError coverage by switching it to system_managed_identity.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
libraries/microsoft-agents-authentication-msal/microsoft_agents/authentication/msal/msal_auth.py Adds user_managed_identity agentic token acquisition via DefaultAzureCredential + fmi_path exchange.
tests/authentication_msal/test_msal_auth.py Adds a unit test for the user_managed_identity fmi_path exchange path and adjusts the unsupported-auth-type test.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +441 to +456
credential = DefaultAzureCredential(**credential_kwargs)
try:
token = await credential.get_token("api://AzureAdTokenExchange/.default")
return token.token
except Exception: # noqa: BLE001 - failures are logged and surfaced as None
logger.exception(
"Failed to acquire agentic application token via DefaultAzureCredential "
"for agent_app_instance_id=%s",
agent_app_instance_id,
)
return None
finally:
try:
await credential.close()
except Exception: # noqa: BLE001 - best-effort cleanup
logger.debug("Error closing DefaultAzureCredential", exc_info=True)
Comment on lines +372 to +375
fake_module = types.ModuleType("azure.identity.aio")
fake_module.DefaultAzureCredential = _FakeCredential
mocker.patch.dict(sys.modules, {"azure.identity.aio": fake_module})

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants