# Microsoft Entra SDK for AgentID: Polyglot Agent Authentication

Containerized companion service that handles token management for AI agents via HTTP — any language, any framework.

> **Preview** — Image: `mcr.microsoft.com/entra-sdk/auth-sidecar:<tag>`. Check [GitHub releases](https://github.com/AzureAD/microsoft-identity-web/releases) for tags.

## Architecture

```
Client App → Your Agent (Python/Node/Go/Java) → Microsoft Entra SDK for AgentID (localhost:5000) → Microsoft Entra ID
                                                                    ↓
                                                              Downstream APIs
```

The Microsoft Entra SDK for AgentID runs as a companion container in the same pod or Docker network. Your agent calls it over HTTP — no SDK embedding required.

### Agent Integration (3P and Custom Agents)

Third-party and custom agents authenticate using the Blueprint → BlueprintPrincipal → AgentIdentity hierarchy. The Microsoft Entra SDK for AgentID acquires tokens for these agent identities — whether you are building your own custom agent or integrating a third-party agent:

```
                        GET /AuthorizationHeaderUnauthenticated/graph
                              ?AgentIdentity={agent-app-id}
┌──────────────────┐   ─────────────────────────────────▶  ┌─────────────────────────────┐
│  Agent           │                                       │  Microsoft Entra SDK        │
│  (any language)  │   ◀─────────────────────────────────  │  for AgentID (:5000)        │
│                  │    { authorizationHeader:             │                             │
└────────┬─────────┘      "Bearer eyJ..." }                │  · AzureAd Config (Tenant,  │
         │                                                 │    ClientId, FIC)           │
         │                                                 │  · Agent Identity Params    │
         │                                                 │  · Downstream API Scopes    │
         │                                                 │  · Token Cache              │
         │                                                 └──────────────┬──────────────┘
         │                                                                │
         │ Authorization: Bearer <JWT>                                    │ OAuth 2.0
         ▼                                                                ▼
┌──────────────────────┐                                   ┌──────────────────────────┐
│  Downstream APIs     │                                   │  Microsoft Entra ID      │
│  · Microsoft Graph   │                                   │  · Blueprint             │
│  · Custom APIs       │                                   │  · AgentIdentity         │
│  · Azure Services    │                                   │                          │
└──────────────────────┘                                   └──────────────────────────┘
```

## Microsoft Entra SDK for AgentID Configuration

### Core Settings

```yaml
env:
- name: AzureAd__Instance
  value: "https://login.microsoftonline.com/"
- name: AzureAd__TenantId
  value: "<your-tenant-id>"
- name: AzureAd__ClientId
  value: "<blueprint-app-id>"
```

### Client Credentials

> **⚠️ Client secrets are for development only.** Production deployments must use Federated Identity Credentials (FIC) via Workload Identity (`SignedAssertionFilePath`) or Managed Identity.

```yaml
# Dev ONLY: Client Secret (do NOT use in production)
- name: AzureAd__ClientCredentials__0__SourceType
  value: "ClientSecret"
- name: AzureAd__ClientCredentials__0__ClientSecret
  value: "<secret>"

# Prod (AKS): Federated Identity Credentials via Workload Identity — RECOMMENDED
- name: AzureAd__ClientCredentials__0__SourceType
  value: "SignedAssertionFilePath"

# Prod (VM/App Service): Managed Identity
- name: AzureAd__ClientCredentials__0__SourceType
  value: "SignedAssertionFromManagedIdentity"
- name: AzureAd__ClientCredentials__0__ManagedIdentityClientId
  value: "<managed-identity-client-id>"
```

### Downstream API

```yaml
- name: DownstreamApis__Graph__BaseUrl
  value: "https://graph.microsoft.com/v1.0/"
- name: DownstreamApis__Graph__Scopes__0
  value: "https://graph.microsoft.com/.default"
- name: DownstreamApis__Graph__RequestAppToken
  value: "true"
```

## Endpoint Reference

| Endpoint | Method | Auth Required | Purpose |
|----------|--------|---------------|---------|
| `/Validate` | GET | Yes | Validate inbound bearer token, return claims |
| `/AuthorizationHeader/{name}` | GET | Yes | Validate inbound token + acquire downstream token (OBO) |
| `/AuthorizationHeaderUnauthenticated/{name}` | GET | No | Acquire app/agent token without inbound user token |
| `/DownstreamApi/{name}` | ANY | Yes | Validate + call downstream API with auto token |
| `/DownstreamApiUnauthenticated/{name}` | ANY | No | Call downstream API with app/agent token |
| `/healthz` | GET | No | Health probe |

### Agent Identity Query Parameters

| Parameter | Purpose | Example |
|-----------|---------|---------|
| `AgentIdentity` | Agent app (client) ID — autonomous mode | `?AgentIdentity=<agent-client-id>` |
| `AgentIdentity` + `AgentUsername` | Interactive mode by UPN | `?AgentIdentity=<id>&AgentUsername=user@contoso.com` |
| `AgentIdentity` + `AgentUserId` | Interactive mode by Object ID | `?AgentIdentity=<id>&AgentUserId=<oid>` |

Rules:
- `AgentUsername`/`AgentUserId` require `AgentIdentity`
- `AgentUsername` and `AgentUserId` are mutually exclusive
- `AgentIdentity` alone = autonomous agent
- `AgentIdentity` + inbound bearer = interactive (OBO) agent

## Code Patterns

### Autonomous 3P Agent (Python)

```python
import os
import requests

SIDECAR_URL = os.environ.get("SIDECAR_URL", "http://localhost:5000")
AGENT_APP_ID = os.environ["AGENT_CLIENT_ID"]

def get_agent_token(downstream_api: str = "Graph") -> str:
    """Acquire autonomous agent token via Microsoft Entra SDK for AgentID."""
    url = f"{SIDECAR_URL}/AuthorizationHeaderUnauthenticated/{downstream_api}"
    resp = requests.get(url, params={"AgentIdentity": AGENT_APP_ID}, timeout=30)
    resp.raise_for_status()
    return resp.json()["authorizationHeader"]

def call_downstream_api(endpoint: str) -> dict:
    """Call a downstream API using agent identity token."""
    token = get_agent_token()
    resp = requests.get(endpoint, headers={"Authorization": token}, timeout=10)
    resp.raise_for_status()
    return resp.json()
```

### Interactive Agent with User Delegation (Python)

```python
def get_delegated_token(user_token: str, downstream_api: str = "Graph") -> str:
    """Acquire delegated token via OBO flow."""
    url = f"{SIDECAR_URL}/AuthorizationHeader/{downstream_api}"
    resp = requests.get(
        url,
        headers={"Authorization": f"Bearer {user_token}"},
        timeout=30,
    )
    resp.raise_for_status()
    return resp.json()["authorizationHeader"]
```

### Autonomous Agent (TypeScript)

```typescript
const SIDECAR_URL = process.env.SIDECAR_URL ?? "http://localhost:5000";
const AGENT_APP_ID = process.env.AGENT_CLIENT_ID!;

async function getAgentToken(downstreamApi = "Graph"): Promise<string> {
  const url = `${SIDECAR_URL}/AuthorizationHeaderUnauthenticated/${downstreamApi}`;
  const res = await fetch(url + `?AgentIdentity=${AGENT_APP_ID}`);
  if (!res.ok) throw new Error(`Microsoft Entra SDK for AgentID error: ${res.status}`);
  const data = await res.json();
  return data.authorizationHeader;
}
```

### Token Validation Middleware

```python
def validate_incoming_token(bearer_token: str) -> dict:
    """Validate an incoming bearer token and extract claims."""
    resp = requests.get(
        f"{SIDECAR_URL}/Validate",
        headers={"Authorization": f"Bearer {bearer_token}"},
        timeout=10,
    )
    resp.raise_for_status()
    return resp.json()["claims"]
```

### Direct Downstream Call via Microsoft Entra SDK for AgentID

```python
def call_graph_me(user_token: str) -> dict:
    """Call Microsoft Graph /me via Microsoft Entra SDK for AgentID proxy."""
    resp = requests.get(
        f"{SIDECAR_URL}/DownstreamApi/Graph",
        params={"optionsOverride.RelativePath": "me"},
        headers={"Authorization": f"Bearer {user_token}"},
        timeout=10,
    )
    resp.raise_for_status()
    return resp.json()["content"]
```

## Deployment

### Docker Compose (Development)

```yaml
version: '3.8'
services:
  sidecar:
    image: mcr.microsoft.com/entra-sdk/auth-sidecar:1.0.0-azurelinux3.0-distroless
    ports:
      - "5001:5000"
    environment:
      - AzureAd__Instance=https://login.microsoftonline.com/
      - AzureAd__TenantId=${TENANT_ID}
      - AzureAd__ClientId=${BLUEPRINT_APP_ID}
      - AzureAd__ClientCredentials__0__SourceType=ClientSecret
      - AzureAd__ClientCredentials__0__ClientSecret=${BLUEPRINT_CLIENT_SECRET}
      - DownstreamApis__Graph__BaseUrl=https://graph.microsoft.com/v1.0/
      - DownstreamApis__Graph__Scopes__0=https://graph.microsoft.com/.default
      - DownstreamApis__Graph__RequestAppToken=true
      - ASPNETCORE_URLS=http://+:5000

  agent:
    build: ./agent
    ports:
      - "3000:3000"
    environment:
      - SIDECAR_URL=http://sidecar:5000
      - AGENT_CLIENT_ID=${AGENT_CLIENT_ID}
    depends_on:
      - sidecar
```

### Kubernetes (Production)

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: agent-deployment
spec:
  replicas: 3
  template:
    metadata:
      labels:
        app: agent
        azure.workload.identity/use: "true"
    spec:
      serviceAccountName: agent-sa
      containers:
      - name: agent
        image: myregistry/agent:latest
        env:
        - name: SIDECAR_URL
          value: "http://localhost:5000"
      - name: sidecar
        image: mcr.microsoft.com/entra-sdk/auth-sidecar:1.0.0
        ports:
        - containerPort: 5000
        env:
        - name: AzureAd__TenantId
          valueFrom:
            configMapKeyRef:
              name: agent-config
              key: tenant-id
        - name: AzureAd__ClientId
          valueFrom:
            configMapKeyRef:
              name: agent-config
              key: client-id
        - name: AzureAd__ClientCredentials__0__SourceType
          value: "SignedAssertionFilePath"
        - name: Kestrel__Endpoints__Http__Url
          value: "http://127.0.0.1:5000"
        resources:
          requests: { memory: "128Mi", cpu: "100m" }
          limits: { memory: "256Mi", cpu: "250m" }
        livenessProbe:
          httpGet: { path: /healthz, port: 5000 }
          initialDelaySeconds: 10
        readinessProbe:
          httpGet: { path: /healthz, port: 5000 }
          initialDelaySeconds: 5
```

## Security

> **⚠️ The Microsoft Entra SDK for AgentID API must NOT be publicly accessible.** Pod-local or same Docker network only.

1. **Bind to localhost** — `Kestrel__Endpoints__Http__Url=http://127.0.0.1:5000`
2. **Never expose via LoadBalancer/Ingress**
3. **Use Workload Identity in AKS** — `SignedAssertionFilePath` over client secrets
4. **Use Key Vault for certificates** — `SourceType=KeyVault` in production
5. **Separate ConfigMap from Secrets** in Kubernetes

## Troubleshooting

| Symptom | Cause | Fix |
|---------|-------|-----|
| 404 on `/AuthorizationHeader/{name}` | `{name}` not in config | Add `DownstreamApis__{name}__BaseUrl` env var |
| 400 `AgentUsername requires AgentIdentity` | Missing `AgentIdentity` param | Always pair user params with `AgentIdentity` |
| 400 `mutually exclusive` | Both `AgentUsername` and `AgentUserId` | Use one or the other |
| 401 on `/Validate` | Invalid/expired inbound token | Check token audience matches `AzureAd__ClientId` |
| 500 token acquisition failure | Wrong creds or missing admin consent | `kubectl logs <pod> -c sidecar` |
| Connection refused | SDK not ready or wrong URL | Verify `SIDECAR_URL` and `/healthz` |

## External Links

| Resource | URL |
|----------|-----|
| SDK Overview | https://learn.microsoft.com/en-us/entra/msidweb/agent-id-sdk/overview |
| Installation | https://learn.microsoft.com/en-us/entra/msidweb/agent-id-sdk/installation |
| Configuration | https://learn.microsoft.com/en-us/entra/msidweb/agent-id-sdk/configuration |
| Endpoints | https://learn.microsoft.com/en-us/entra/msidweb/agent-id-sdk/endpoints |
| Agent Identities | https://learn.microsoft.com/en-us/entra/msidweb/agent-id-sdk/agent-identities |
| Security | https://learn.microsoft.com/en-us/entra/msidweb/agent-id-sdk/security |
| OpenAPI Spec | https://github.com/AzureAD/microsoft-identity-web/blob/master/src/Microsoft.Identity.Web.Sidecar/OpenAPI/Microsoft.Identity.Web.AgentID.json |
