# OAuth2 Token Flow Source: [Agent ID Setup Instructions](https://learn.microsoft.com/en-us/entra/agent-id/identity-platform/agent-id-setup-instructions) Agent Identities authenticate at runtime using credentials configured on the **Blueprint** (not on the Agent Identity — Agent Identities cannot hold credentials). | Option | Use case | Credential type | |--------|----------|-----------------| | **Managed Identity + WIF** | Production (Azure-hosted) | Federated Identity Credential on Blueprint | | **Client secret** | Local dev / testing | Password credential on Blueprint | Both options feed the two-step `fmi_path` exchange in [runtime-token-exchange.md](runtime-token-exchange.md). --- ## Option A: Managed Identity + Workload Identity Federation (Production) ### 1. Set the Application ID URI on the Blueprint ```python requests.patch( f"{GRAPH}/applications/{blueprint_obj_id}", headers=headers, json={"identifierUris": [f"api://{blueprint_app_id}"]}, ).raise_for_status() ``` ### 2. Create a Federated Identity Credential on the Blueprint Use the typed path — FICs go on the Blueprint, not on the Agent Identity SP: ```python fic_body = { "name": "my-fic-name", "issuer": f"https://login.microsoftonline.com/{tenant_id}/v2.0", "subject": mi_principal_id, # The MI's object ID (principalId), NOT client ID "audiences": ["api://AzureADTokenExchange"], } requests.post( f"{GRAPH}/applications/{blueprint_obj_id}" f"/microsoft.graph.agentIdentityBlueprint/federatedIdentityCredentials", headers=headers, json=fic_body, ).raise_for_status() ``` ### 3. Acquire a token from the caller ```python from azure.identity import ManagedIdentityCredential cred = ManagedIdentityCredential(client_id=MI_CLIENT_ID) token = cred.get_token(f"api://{blueprint_app_id}/.default") # Authorization: Bearer {token.token} ``` ### 4. Validate on the backend ```python import jwt from jwt import PyJWKClient jwks_client = PyJWKClient( f"https://login.microsoftonline.com/{tenant_id}/discovery/v2.0/keys" ) signing_key = jwks_client.get_signing_key_from_jwt(token_str) claims = jwt.decode( token_str, signing_key.key, algorithms=["RS256"], audience=f"api://{blueprint_app_id}", issuer=f"https://sts.windows.net/{tenant_id}/", ) ``` ### Key Rules (WIF) - **FICs go on the Blueprint** using the typed path (`.../microsoft.graph.agentIdentityBlueprint/federatedIdentityCredentials`). - **`subject`** is the MI's `principalId` (object ID), not its client ID. - **`audiences`** must be `["api://AzureADTokenExchange"]` — not your API audience. - **FIC `issuer`**: `https://login.microsoftonline.com/{tenant}/v2.0`. - **Token `issuer`** for validation: `https://sts.windows.net/{tenant}/` (different domain, trailing slash). --- ## Option B: Client Secret (Local Dev) ### 1. Add a password credential to the Blueprint PowerShell: ```powershell $body = @{ "passwordCredential" = @{ "displayName" = "Dev Secret" "endDateTime" = "2027-01-01T00:00:00Z" } } $credential = Invoke-MgGraphRequest -Method POST ` -Uri "https://graph.microsoft.com/v1.0/applications//addPassword" ` -Headers @{ "OData-Version" = "4.0"; "Content-Type" = "application/json" } ` -Body ($body | ConvertTo-Json -Depth 5) -OutputType PSObject $credential.secretText # Save NOW — not retrievable later ``` Python: ```python resp = requests.post( f"{GRAPH}/applications/{blueprint_obj_id}/addPassword", headers=headers, json={"passwordCredential": { "displayName": "Dev Secret", "endDateTime": "2027-01-01T00:00:00Z", }}, ) resp.raise_for_status() secret_text = resp.json()["secretText"] # Save NOW ``` ### 2. Drive the two-step exchange Pass `blueprint_secret=secret_text` into `get_parent_token(...)` from [runtime-token-exchange.md](runtime-token-exchange.md), then call `exchange_autonomous` or `exchange_obo`. ### Key Rules (Client Secret) - **Save `secretText` immediately** — it can't be retrieved later. - **Secrets belong on the Blueprint only** — Agent Identities can't have password credentials (`PropertyNotCompatibleWithAgentIdentity`). - **Not for production** — use MI + WIF. - **Respect org credential-lifetime policy** when setting `endDateTime`. - Don't use `DefaultAzureCredential` to acquire Blueprint tokens — Azure CLI tokens carry `Directory.AccessAsUser.All` and are rejected. Use `ClientSecretCredential` or the raw HTTP exchange.