> ## Documentation Index
> Fetch the complete documentation index at: https://docs.getbifrost.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Microsoft Entra

> Map Azure managed identities and app registrations to Bifrost service accounts so AKS services, Azure Functions and CI pipelines call Bifrost with their Entra token.

With Microsoft Entra, a workload calls Bifrost with an **app-only access token** Entra issues for the Bifrost SSO app registration. This covers AKS services using Azure workload identity, Azure VMs and Functions with a managed identity, and Azure DevOps or GitHub pipelines that sign in to Entra with federated credentials. The token's `oid` claim, the object ID of the workload's service principal, is what you map to a service account.

> [!NOTE]
>
> Workload identity reuses your Entra SSO configuration: the same tenant and app registration. Set up [Entra SSO](https://docs.getbifrost.ai/enterprise/setting-up-entra/oidc.md) first. Multi-tenant (`common`) configurations are not supported.

## Prerequisites

* Microsoft Entra SSO configured in Bifrost, with the app registration's **Application.Read.All** Graph permission granted if you want to pick workloads from Entra in the dashboard.
* A service account in Bifrost with an access profile for the workload.
* In Bifrost, the **User Provisioning → `UpdateWorkloadIdentities`** permission to map workloads and search Entra for them.

## Step 1: Expose the Bifrost app to applications

Workloads request a token for the Bifrost app registration, so it needs an identifier.

1. In the Entra admin center, open **App registrations** and select the Bifrost SSO app.
2. Under **Expose an API**, set the **Application ID URI**. Entra suggests `api://<client ID>`, which Bifrost accepts as is. If you choose another, such as `api://bifrost`, enter the same value as **App ID URI** in Bifrost's Entra SSO settings.

## Step 2: Assign the workload to the Bifrost app

The [Entra SSO setup](https://docs.getbifrost.ai/enterprise/setting-up-entra/oidc.md) turns on **Assignment required** for the Bifrost enterprise application, so Entra issues a token for Bifrost only to people and apps assigned to it. A workload is assigned through an app role:

1. In the Bifrost app registration, open **App roles** and create a role with **Allowed member types** set to **Applications**, for example with the value `Bifrost.Workload`.
2. Assign that role to the workload's service principal. Managed identities can't be assigned in the portal, so use Microsoft Graph:
   ```bash theme={null}
   BIFROST_SP=$(az ad sp show --id <Bifrost client ID> --query id -o tsv)
   ROLE_ID=$(az ad sp show --id <Bifrost client ID> --query "appRoles[?value=='Bifrost.Workload'].id" -o tsv)
   az rest --method POST \
     --uri "https://graph.microsoft.com/v1.0/servicePrincipals/<workload object ID>/appRoleAssignments" \
     --body "{\"principalId\": \"<workload object ID>\", \"resourceId\": \"$BIFROST_SP\", \"appRoleId\": \"$ROLE_ID\"}"
   ```
   The workload object ID is the one you map in the next step.

Without the assignment, Entra refuses the workload's token request with `AADSTS501051`.

## Step 3: Map the workload to a service account

1. Open the service account under **Governance → Users** and go to **Workload identity**.
2. Select **Add workload identity** and choose **Azure workload identity federation**.
3. Search Entra for the managed identity or app by name, or paste its service principal's object ID. Use the object ID, not the application (client) ID.
4. Select **Add**. The workload can call Bifrost straight away; its requests appear in the logs under the service account.

For a managed identity, the object ID is its **principal ID** (`az identity show --name <identity> --resource-group <rg> --query principalId -o tsv`). For an app registration, it is the object ID of its service principal under **Enterprise applications** (`az ad sp show --id <app client ID> --query id -o tsv`), not the app registration's own object ID.

To cut a workload off, turn its mapping off or remove it.

## Step 4: Call Bifrost from the workload

The examples below request `api://bifrost/.default`, which assumes the App ID URI `api://bifrost`. Replace it with your own App ID URI plus `/.default`, such as `api://<client ID>/.default` if you kept Entra's suggestion; Bifrost shows the scope to use.

The workload requests a token for the Bifrost app, using the scope shown in Bifrost (the App ID URI or client ID with `/.default`), and sends it as the bearer token. It has to sign in as itself, the managed identity or app you mapped: a token a person gets after a normal sign-in carries that person's identity, not the workload's, and goes through the normal sign-in path instead.

> [tab] Python (AKS, VM, Functions)
>
> ```python theme={null}
> from azure.identity import DefaultAzureCredential
> import requests
>
> token = DefaultAzureCredential().get_token("api://bifrost/.default").token
>
> requests.post(
>     "https://bifrost.example.com/v1/chat/completions",
>     headers={"Authorization": f"Bearer {token}"},
>     json={"model": "openai/gpt-4o-mini", "messages": [{"role": "user", "content": "ping"}]},
> )
> ```
>
> On AKS, a pod gets the managed identity's token through Azure workload identity:
>
> 1. Turn on the OIDC issuer and workload identity on the cluster (`az aks update --name <cluster> --resource-group <rg> --enable-oidc-issuer --enable-workload-identity`), and read its issuer URL:
>    ```bash theme={null}
>    az aks show --name <cluster> --resource-group <rg> --query oidcIssuerProfile.issuerUrl -o tsv
>    ```
> 2. Add a federated credential to the managed identity that trusts the pod's Kubernetes service account:
>    ```bash theme={null}
>    az identity federated-credential create --name payments-api \
>      --identity-name <identity> --resource-group <rg> \
>      --issuer <cluster OIDC issuer URL> \
>      --subject system:serviceaccount:<namespace>:<service account> \
>      --audience api://AzureADTokenExchange
>    ```
> 3. Annotate that Kubernetes service account with `azure.workload.identity/client-id: <managed identity client ID>`.
> 4. Run the pod as that service account (`spec.serviceAccountName: <service account>` in the pod template) and label it `azure.workload.identity/use: "true"`. A pod running as any other service account gets a token whose subject the federated credential does not trust.
>
> `DefaultAzureCredential` then picks the identity up without a secret. On a VM or in Functions, enable the managed identity on the resource; no federated credential is needed. If the identity you mapped is **user-assigned**, also tell the credential which one to use: set the `AZURE_CLIENT_ID` environment variable to its client ID, or pass `DefaultAzureCredential(managed_identity_client_id="<managed identity client ID>")`. Otherwise it uses the resource's system-assigned identity, whose token carries a different `oid`, or fails to get a token when there is none.

> [tab] Azure CLI
>
> Sign in as the workload first, not as yourself. On an Azure resource with a managed identity:
>
> ```bash theme={null}
> az login --identity --allow-no-subscriptions
> ```
>
> With a user-assigned managed identity, name it, or the system-assigned identity signs in instead:
>
> ```bash theme={null}
> az login --identity --client-id <managed identity client ID> --allow-no-subscriptions
> ```
>
> As an app registration with a federated credential, for example in a pod or pipeline that has an OIDC token in a file:
>
> ```bash theme={null}
> az login --service-principal --username <app client ID> --tenant <tenant ID> \
>   --federated-token "$(cat "$AZURE_FEDERATED_TOKEN_FILE")" --allow-no-subscriptions
> ```
>
> Then request the token for Bifrost:
>
> ```bash theme={null}
> TOKEN=$(az account get-access-token --scope api://bifrost/.default --query accessToken -o tsv)
> curl https://bifrost.example.com/v1/chat/completions \
>   -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
>   -d '{"model": "openai/gpt-4o-mini", "messages": [{"role": "user", "content": "ping"}]}'
> ```
>
> Map the managed identity's principal ID, or the app's service principal object ID: the token's `oid`.

## Troubleshooting

**401 invalid workload identity token.** The token is expired, was issued for another app, or is valid for over 24 hours. Check that the scope is the Bifrost app's App ID URI or client ID with `/.default`. If the app registration issues v1 tokens (`accessTokenAcceptedVersion` unset or 1), the token's audience is the App ID URI: Bifrost accepts `api://<client ID>` on its own, and any other App ID URI once it is set in Bifrost's Entra SSO settings.

**403 not connected to a Bifrost service account.** The message includes the token's object ID. Map it to a service account.

**Entra refuses the token request with `AADSTS501051`.** The Bifrost enterprise application has **Assignment required** turned on, and the workload isn't assigned to it. Assign it an app role on the Bifrost app, as in [Step 2](#step-2-assign-the-workload-to-the-bifrost-app). Don't turn **Assignment required** off to get around this: that also lets every person in your tenant sign in to Bifrost through SSO, not only the people assigned to the app.

**The workload isn't in the Entra search.** Without a search, Bifrost lists managed identities and apps registered in your tenant; search by name to find others, or paste the object ID. The list needs the app registration's **Application.Read.All** permission.

**A person's SSO stops working.** It shouldn't: workload identity only takes app-only tokens. A person's token always carries the `scp` claim and goes through the normal sign-in path.

**The token works but runs as a person, or is refused as one.** It was requested after a person's sign-in, such as a plain `az login`. Sign in as the managed identity or app instead, as in Step 4.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.