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

# Microsoft Azure

> Call Reducto from Azure with managed identity tokens or AKS service account tokens instead of a stored API key.

Azure workloads can use either of two token sources:

* **Azure managed identity**: a VM or virtual machine scale set requests a Microsoft Entra ID token from the Azure Instance Metadata Service (IMDS).
* **Azure Kubernetes Service (AKS)**: a pod uses a projected Kubernetes service account token issued by the cluster.

App Service, Azure Functions and Container Apps do not use IMDS. They give the managed identity token at the URL in the `IDENTITY_ENDPOINT` environment variable, with the `X-IDENTITY-HEADER` request header set to `IDENTITY_HEADER`. The SDK managed identity helpers do not support this endpoint. On these services, pass your own token provider that calls it. See Microsoft's guide to [managed identities for App Service and Azure Functions](https://learn.microsoft.com/en-us/azure/app-service/overview-managed-identity#rest-endpoint-reference).

## Azure managed identity

### Set up the identity

Managed identity tokens are issued for a resource, and the resource becomes the token's `aud`. Create an app registration in Microsoft Entra ID to represent Reducto, and set its **Application ID URI**, for example `api://reducto-acme-prod`. See Microsoft's guide to [register an application](https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-register-app).

The Application ID URI, the IMDS `resource` parameter, the token's `aud` and the Reducto provider audience must all be the same value.

Assign a system-assigned or user-assigned [managed identity](https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/overview) to the resource that runs your code.

### Get and inspect a token

From the Azure resource:

```bash theme={null}
SUBJECT_TOKEN=$(curl -sS -G -H "Metadata: true" \
  "http://169.254.169.254/metadata/identity/oauth2/token" \
  --data-urlencode "api-version=2018-02-01" \
  --data-urlencode "resource=api://reducto-acme-prod" \
  | jq -r .access_token)
cut -d. -f2 <<< "$SUBJECT_TOKEN" | tr '_-' '/+' | base64 -d 2>/dev/null | jq .
```

If the resource has more than one user-assigned identity, add `client_id=<identity client ID>` to the request.

```json theme={null}
{
  "iss": "https://sts.windows.net/4c2d8e1f-7a3b-4f6e-9d0c-1b2a3c4d5e6f/",
  "aud": "api://reducto-acme-prod",
  "tid": "4c2d8e1f-7a3b-4f6e-9d0c-1b2a3c4d5e6f",
  "appid": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
  "oid": "1f2e3d4c-5b6a-4978-8695-a4b3c2d1e0f9",
  "sub": "1f2e3d4c-5b6a-4978-8695-a4b3c2d1e0f9",
  "xms_mirid": "/subscriptions/0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d/resourcegroups/payments/providers/Microsoft.ManagedIdentity/userAssignedIdentities/invoice-parser",
  "iat": 1793322245,
  "nbf": 1793322245,
  "exp": 1793408945
}
```

The issuer depends on the token version that the app registration accepts. Version 1 tokens use `https://sts.windows.net/<tenant ID>/` and version 2 tokens use `https://login.microsoftonline.com/<tenant ID>/v2.0`. Copy `iss` from a real token.

| Claim | Meaning |
| - | - |
| `tid` | The Entra tenant ID. |
| `appid` | The managed identity's client ID. Stable for the life of the identity. |
| `oid`, `sub` | The managed identity's object ID. |
| `xms_mirid` | The Azure resource ID of the identity. |

### Configure Reducto

In [Studio](https://studio.reducto.ai), open **Settings > Workload identity** and create a provider with the **Azure managed identity** preset:

| Field | Value |
| - | - |
| **Name** | `azure-prod` |
| **Issuer** | The token's `iss`, for example `https://sts.windows.net/4c2d8e1f-7a3b-4f6e-9d0c-1b2a3c4d5e6f/` |
| **Audience** | `api://reducto-acme-prod` |
| **Key source** | OIDC discovery |

Add a mapping for the identity and tenant:

| Key | Value |
| - | - |
| `tid` | `4c2d8e1f-7a3b-4f6e-9d0c-1b2a3c4d5e6f` |
| `appid` | `9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d` |

If your tokens have no `appid`, use `azp` (version 2 tokens) or `oid`.

### Call Reducto

Pass the Application ID URI to the token provider as `resource`. The default resource is `https://management.azure.com/`, which Reducto rejects. For a user-assigned identity on a resource with several identities, also pass `client_id` (`clientId` in Node.js).

```python Python theme={null}
import os

from reducto import Reducto
from reducto.auth import azure_managed_identity_token_provider

client = Reducto(
    workload_identity={
        "identity_provider_id": os.environ["REDUCTO_IDENTITY_PROVIDER_ID"],
        "provider": azure_managed_identity_token_provider("api://reducto-acme-prod"),
    }
)

result = client.parse.run(input="https://cdn.reducto.ai/samples/fidelity-example.pdf")
print(result.result.chunks[0].content[:500])
```

```javascript Node.js theme={null}
import Reducto from "reductoai";
import { azureManagedIdentityTokenProvider } from "reductoai/auth";

const client = new Reducto({
  workloadIdentity: {
    identityProviderId: process.env.REDUCTO_IDENTITY_PROVIDER_ID,
    provider: azureManagedIdentityTokenProvider({ resource: "api://reducto-acme-prod" }),
  },
});

const result = await client.parse.run({
  input: "https://cdn.reducto.ai/samples/fidelity-example.pdf",
});
console.log(result.result.chunks[0].content.slice(0, 500));
```

```bash cURL theme={null}
SUBJECT_TOKEN=$(curl -sS -G -H "Metadata: true" \
  "http://169.254.169.254/metadata/identity/oauth2/token" \
  --data-urlencode "api-version=2018-02-01" \
  --data-urlencode "resource=api://reducto-acme-prod" \
  | jq -r .access_token)

ACCESS_TOKEN=$(jq -n \
  --arg subject_token "$SUBJECT_TOKEN" \
  --arg provider_id "$REDUCTO_IDENTITY_PROVIDER_ID" \
  '{
    grant_type: "urn:ietf:params:oauth:grant-type:token-exchange",
    subject_token_type: "urn:ietf:params:oauth:token-type:jwt",
    subject_token: $subject_token,
    identity_provider_id: $provider_id
  }' \
  | curl -sS --fail-with-body -X POST "https://platform.reducto.ai/oauth/token" \
      -H "Content-Type: application/json" -d @- \
  | jq -r .access_token)

curl -X POST "https://platform.reducto.ai/parse" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"input":"https://cdn.reducto.ai/samples/fidelity-example.pdf"}'
```

Entra managed identity tokens often last up to 24 hours. Reducto still caps the access token at the mapping lifetime, at most one hour.

## Azure Kubernetes Service (AKS)

AKS can publish a public OIDC issuer for the cluster. Get it, and enable it if it is empty:

```bash theme={null}
az aks show --name payments-prod --resource-group payments \
  --query "oidcIssuerProfile.issuerUrl" --output tsv

az aks update --name payments-prod --resource-group payments --enable-oidc-issuer
```

Create a service account and mount a projected token with the Reducto audience, as in the [Kubernetes guide](https://docs.reducto.ai/security/workload-identity-federation/kubernetes.md#set-up-the-cluster). AKS tokens use the standard Kubernetes claims:

```json theme={null}
{
  "iss": "https://westus2.oic.prod-aks.azure.com/4c2d8e1f-7a3b-4f6e-9d0c-1b2a3c4d5e6f/7e6d5c4b-3a29-4180-9f7e-6d5c4b3a2918/",
  "aud": ["https://platform.reducto.ai"],
  "sub": "system:serviceaccount:payments:reducto-client",
  "iat": 1793322245,
  "exp": 1793325845,
  "kubernetes.io": {
    "namespace": "payments",
    "serviceaccount": {
      "name": "reducto-client",
      "uid": "2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a"
    }
  }
}
```

Create a provider with the **AKS** preset:

| Field | Value |
| - | - |
| **Name** | `aks-payments-prod` |
| **Issuer** | The issuer URL from `az aks show`, including the trailing slash |
| **Audience** | `https://platform.reducto.ai` |
| **Key source** | OIDC discovery |

Add a mapping with `sub` = `system:serviceaccount:payments:reducto-client`. Then use `k8s_service_account_token_provider("/var/run/secrets/reducto/token")` (Python) or `k8sServiceAccountTokenProvider("/var/run/secrets/reducto/token")` (Node.js), as shown in the [Kubernetes guide](https://docs.reducto.ai/security/workload-identity-federation/kubernetes.md#call-reducto-from-the-pod).

If your pods already use [Microsoft Entra Workload ID](https://learn.microsoft.com/en-us/azure/aks/workload-identity-overview) to get Entra tokens for a managed identity, you can follow the [managed identity](#azure-managed-identity) steps instead. Reducto then trusts the Entra token, not the cluster token.

## Azure best practices

* Use one managed identity per application and environment. Match its `appid` together with `tid`.
* Use a dedicated app registration for the Reducto audience, and do not reuse it for other APIs.
* Keep development, staging and production on separate identities and mappings.
* Remove mappings for identities that you delete, and review them when you move resources between subscriptions.
