Third-party agents and federation
Bring Bedrock, n8n, Ollama, and SPIFFE-based agents under one Entra governance plane.
Why this matters
Real enterprises don't run only Microsoft agents. MTN, for example, has AWS Bedrock Claude agents, n8n workflow agents, Ollama-based local LLM prototypes, and LangChain agents in Azure Container Apps. Bringing all of them under one governance plane (Entra Agent ID + Agent 365) means every agent — regardless of who built it or where it runs — appears in the registry, is bound to a blueprint, honours Conditional Access, is watched by ID Protection, and shows up in Purview audit.
Microsoft supports this via two patterns: the sidecar and Workload Identity Federation (WIF). This lesson makes them concrete.
Cross-ref: Lesson 2.2 (workload identity — SPIFFE / SPIRE, AWS IRSA, GCP WIF, Azure Workload Identity) is the pre-requisite mental model. This lesson shows how those primitives federate into Entra Agent ID.
Learning objectives
- Choose between sidecar and WIF for a given third-party agent.
- Deploy the Entra ID Auth SDK sidecar alongside a Bedrock, n8n, or Ollama agent.
- Configure Federated Identity Credentials on a blueprint to trust AWS STS / GCP WIF / SPIFFE.
- Migrate an existing SPIFFE-based agent to Entra Agent ID without losing local mTLS.
- Combine Microsoft-native governance with your existing multi-cloud identity fabric.
1. Sidecar vs Workload Identity Federation
Two patterns; pick per agent workload.
| Aspect | Sidecar | WIF (Workload Identity Federation) |
|---|---|---|
| Where credentials live | Sidecar container | External identity provider (AWS STS / GCP / SPIFFE) |
| Extra container? | Yes | No |
| Agent code changes | Minimal — HTTP call to localhost:7000 | Larger — agent must fetch and exchange the federated token |
| Best for | Containerised agents on Docker / K8s (Bedrock, LangChain, n8n, Ollama) | Native cloud agents already using AWS STS / GCP WIF |
| Local dev friendliness | Excellent — Docker Compose | Poor — needs federation set up first |
| Language-agnostic | Yes | Depends on client library |
| Recommended default | Yes (for third-party agents) | Only when you already have federation |
Rule of thumb: if the agent is already emitting OIDC tokens from a trusted issuer (AWS STS, GCP WIF, GitHub Actions OIDC, SPIFFE JWT-SVID), use WIF. Otherwise, use the sidecar.
2. Sidecar architecture (recap + third-party angle)
Same sidecar as Lesson 10.3, but running alongside a non-Microsoft agent.
+---------------------------+ HTTP +----------------------------+
| Bedrock agent container |----------------->| Entra ID Auth SDK sidecar |
| (Claude 3, Titan, custom) | /Validate | |
| | /AuthorizationH | - holds blueprint FIC |
| localhost: | | - talks to Entra token |
| 7000/token |<-----------------| - caches + refreshes |
| | Bearer … | |
+---------------------------+ +--------------+-------------+
|
v
Microsoft Entra ID
token endpointThe agent code never sees a Microsoft SDK, an Entra secret, or a certificate. It just calls its local sidecar for tokens.
2.1 Bedrock agent + sidecar (AWS ECS Fargate)
Deployment shape:
- Task definition with two containers:
bedrock-agent+entra-auth-sidecar. - Both share the task's networking; the agent hits
http://localhost:7000/AuthorizationHeader/Graph. - Sidecar authenticates as the blueprint using either (a) a certificate stored in AWS Secrets Manager + rotated, or (b) FIC → AWS STS (Section 3).
Example agent snippet (Python running inside Bedrock invocation):
import httpx
async def call_graph(scope: str, agent_client_id: str) -> dict:
async with httpx.AsyncClient(base_url="http://localhost:7000") as sidecar:
header = (await sidecar.get(
"/AuthorizationHeader/Graph",
params={"AgentIdentity": agent_client_id}
)).text
async with httpx.AsyncClient() as graph:
r = await graph.get(
"https://graph.microsoft.com/v1.0/users?$top=5",
headers={"Authorization": header}
)
return r.json()That's the entire integration.
2.2 n8n agent + community node
n8n has a community node (n8n-nodes-entraagentid) that wraps the token acquisition for you. You register the node in your n8n instance, configure it with the agent identity's client ID + the sidecar endpoint, then any downstream HTTP node uses the acquired token.
Recommended deployment for n8n:
- Azure Container Apps with
azd(Azure Developer CLI) template — the Microsoft team has a reference deployment. - Or your existing n8n hosting + the sidecar as a separate container in the same pod / task.
2.3 Ollama + local development
Local development is where the sidecar shines. Docker Compose brings both up:
services:
ollama:
image: ollama/ollama:latest
ports: ["11434:11434"]
volumes: ["ollama-data:/root/.ollama"]
agent:
build: ./agent
environment:
SIDECAR_URL: http://sidecar:7000
OLLAMA_URL: http://ollama:11434
AGENT_IDENTITY_CLIENT_ID: ${AGENT_IDENTITY_CLIENT_ID}
depends_on: [ollama, sidecar]
sidecar:
image: mcr.microsoft.com/entra/auth-sdk-sidecar:latest
environment:
BLUEPRINT_CLIENT_ID: ${BLUEPRINT_CLIENT_ID}
TENANT_ID: ${TENANT_ID}
# In dev — client secret; prod uses FIC + certs
BLUEPRINT_CLIENT_SECRET: ${BLUEPRINT_CLIENT_SECRET}
ports: ["7000:7000"]
volumes:
ollama-data:docker compose up, and your Ollama-backed LangChain agent immediately has enterprise-grade auth against Entra Agent ID.
3. Workload Identity Federation to Entra
Skip the sidecar; let the external issuer's OIDC tokens directly exchange for Entra tokens.
3.1 AWS STS → Entra
Agent runs on AWS (EC2 / ECS / Lambda with an IAM role).
Agent fetches an OIDC token from AWS STS (
AssumeRoleWithWebIdentityreturns a JWT, or use IRSA + IAM Roles Anywhere).Configure a Federated Identity Credential on the blueprint:
- Issuer:
https://token.actions.aws.com(or your STS OIDC endpoint). - Subject: the ARN of the IAM role, e.g.
arn:aws:iam::123456789012:role/bedrock-agent-role. - Audiences:
api://AzureADTokenExchange.
- Issuer:
Agent exchanges the AWS OIDC token at Entra
/token:POST https://login.microsoftonline.com/<tenant>/oauth2/v2.0/token grant_type=client_credentials client_id=<blueprint client id> client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer client_assertion=<AWS STS OIDC token> scope=https://graph.microsoft.com/.defaultEntra validates the AWS OIDC token via configured FIC, issues a blueprint-scoped token, and — through the impersonation chain — you get an agent identity token.
3.2 GCP Workload Identity → Entra
Analogous:
- Configure the Google Workload Identity Federation pool to trust the target GCP resource.
- On the blueprint, add an FIC trusting
https://accounts.google.comwith the subject = the GCP SA email. - Agent uses google-auth to obtain an OIDC token, exchanges at Entra.
3.3 SPIFFE / SPIRE → Entra (your Module 2 setup)
If your infra already runs SPIFFE / SPIRE (Lesson 2.2):
- Agent fetches a JWT-SVID from the workload API (
WorkloadApiClient().fetch_jwt_svid(audiences=[ENTRA_TENANT])). - On the Entra blueprint, add an FIC trusting your SPIRE trust domain's OIDC endpoint (SPIRE 1.5+ exposes an OIDC discovery URL). Subject = the SPIFFE ID, e.g.
spiffe://mtn.co.za/agents/crm-helpdesk-v2. - Agent exchanges JWT-SVID at Entra with
client_assertion_type=jwt-bearer.
Result: the same SPIFFE identity works for local mTLS (SPIRE cert) and Entra Graph access (Entra token). No secret sprawl.
3.4 GitHub Actions OIDC → Entra (bonus)
The same FIC mechanism lets your GitHub Actions CI/CD create + rotate blueprints without any static Azure credential. Add an FIC with:
- Issuer:
https://token.actions.githubusercontent.com. - Subject:
repo:mtn/agents-repo:ref:refs/heads/main(or environment-specific). - Audiences:
api://AzureADTokenExchange.
Then azure/login@v2 with client-id and tenant-id (no client secret) works.
4. Migrating an existing SPIFFE/OAuth agent to Entra Agent ID
You have an agent that already uses SPIFFE + Keycloak (per Modules 2-3). Here's how to onboard it:
- Create the Entra blueprint for its class. Add an FIC trusting your SPIRE trust domain, subject = the agent's SPIFFE ID.
- Create an agent identity under that blueprint. Populate sponsors, owners, custom security attributes.
- Deploy the sidecar (or add a Python line to your agent to do the JWT-bearer exchange itself).
- Grant permissions on the blueprint principal for the Graph / Azure resources the agent will touch.
- Update CI/CD to also register the agent in the M365 admin center registry (so it's not shadow).
- Keep Keycloak in front of your own APIs — the SPIFFE JWT still authenticates for those. You've simply added Entra as a second consumer of the SPIFFE identity.
Result: your agent authenticates once (SPIFFE) and issues tokens against both stacks. Governance / observability / risk detection from Microsoft + policy enforcement from your existing infra. Best of both.
5. Where the sidecar deploys — cheat sheet
| Platform | Deployment pattern |
|---|---|
| Azure Container Apps | Add sidecar as a second container in the app definition; both reachable at localhost |
| AKS / Kubernetes | Same pod as the agent container, localhost:7000 |
| AWS ECS Fargate | Task definition with two containers; agent + sidecar |
| AWS EKS | Same pod as agent (K8s pattern) |
| GCP Cloud Run | Multi-container Cloud Run service (2024+) |
| GKE | Same pod as agent |
| On-prem K8s | Same pod, standard sidecar pattern |
| Docker Compose (dev) | Two services, shared network |
| VM | Docker run alongside the agent process |
| Serverless (Lambda / Cloud Functions) | Sidecar is problematic — use WIF instead |
Rule: never expose port 7000 outside the pod / task. The sidecar is a private helper, not a public service.
6. Security best practices for the sidecar
From the Microsoft best-practices doc + hard lessons:
- Never embed credentials in agent code. All auth belongs in the sidecar (or in WIF at the platform layer).
- Least privilege on Agent Identities. Grant only Graph / Azure permissions the agent needs. Blocked Graph permissions (Lesson 10.4) still apply.
- Validate token audience and issuer on every downstream call. The receiver of the token should check
iss(Entra tenant),aud(the resource),roles/scp(the permission). - Rotate FIC credentials. Managed identity handles this automatically; certificates need a rotation schedule; client secrets should not exist in prod.
- Monitor token usage. Entra sign-in logs give you every acquisition — feed to Splunk / Sentinel.
- Keep the sidecar updated. Microsoft ships security + compatibility updates regularly. Pin the digest, not
latest, in prod. - Network-isolate the sidecar. Only the agent container talks to it. Deny all other inbound.
7. Troubleshooting playbook
| Symptom | Likely cause | Fix |
|---|---|---|
| Agent can't reach sidecar | Network / DNS / sidecar not running | Verify sidecar container is up; check port 7000; check compose network name |
| Sidecar fails to acquire token | Blueprint auth failed | Verify blueprint client id + credential; check tenant id; check FIC issuer / subject match |
| Sidecar 401 | Missing FIC or wrong subject | Recheck FIC issuer + subject; check MI principal id matches subject |
| Token 401 at Graph | Missing permission or wrong scope | Verify blueprint principal has consented permission; ensure .default or explicit scope matches |
| Token issued but resource 403 | Permission blocked by Entra (Lesson 10.4) | Confirm not in blocked-permission list; check inheritable permissions actually cascaded |
| CA blocking | Report-only CA policy escalated to on | Check CA sign-in logs for policyId; adjust filter |
| Sidecar container OOM | Cache growth | Configure token cache TTL; scale RAM |
| Bedrock agent works locally, fails in ECS | ECS task role doesn't have the STS OIDC config | If using WIF pattern, verify task role assumes the identity that matches the FIC subject |
8. Common pitfalls
- Trying to use client secrets from a Bedrock agent — works, but wildly insecure and fails ISO 42001. Use FIC → AWS STS.
- Two sidecars in one pod — pick one; multiple sidecars produce inconsistent caching and confuse observability.
- Exposing the sidecar port publicly — instant credential exfiltration risk. Bind to
localhostonly. - Not creating an agent identity per environment — one blueprint, one dev + one prod identity minimum.
- Skipping the M365 admin registry entry — your third-party agent becomes a shadow agent (Lesson 10.7). Register it.
- SPIFFE trust domain not exposed via OIDC — SPIRE 1.5+ has an OIDC discovery URL; older SPIRE needs an upgrade or a middleware.
- FIC configured for
refs/heads/mainwhen the CI job runs onrefs/heads/release/*— exchange fails with cryptic "AADSTS70021: No matching federated identity record found". Match subject exactly. - Assuming a single blueprint per agent framework — you can (and often should) have one blueprint per (framework × environment × business unit) combination to keep credentials + policies isolated.
9. Hands-on lab (3 h)
Part A — Sidecar + Ollama (local)
- Clone the Entra Auth SDK sidecar sample.
- Create a test blueprint + agent identity (Lesson 10.2).
- Author
docker-compose.ymlwith three services: Ollama, sidecar, and a small LangChain agent. - Send a chat request that triggers a Graph call; verify from the sidecar logs that the token was acquired and the Graph call succeeded.
Part B — Sidecar + Bedrock (AWS)
- Create an ECS task definition with your Bedrock invocation container + the sidecar container.
- Deploy to Fargate. Ensure the ECS task role has permission to fetch the blueprint's certificate from AWS Secrets Manager.
- Register the sidecar as the agent's identity provider.
- Have the Bedrock agent invoke Graph via the sidecar.
Part C — WIF from AWS STS
- Configure an FIC on the blueprint trusting your AWS OIDC issuer + IAM role ARN.
- Modify the agent to fetch its AWS STS OIDC token and exchange directly at Entra.
- Remove the sidecar entirely from part B; verify the agent still works.
- Compare deployment complexity + audit trail between the two patterns.
Part D — SPIFFE (bonus)
- Deploy SPIRE (per Lesson 2.2).
- Enable SPIRE's OIDC discovery endpoint.
- Add an FIC on the blueprint trusting the SPIRE trust domain.
- Agent fetches a JWT-SVID and exchanges at Entra.
- Verify the same SPIFFE identity is now valid for mTLS to your internal APIs and for Entra Graph.
10. Self-check
- When should you pick sidecar over WIF? Vice versa.
- What are the two conditions for a Federated Identity Credential to match at exchange time?
- Which port does the sidecar bind to by default? Where should it be reachable from?
- Why is Microsoft's own guidance to avoid client secrets in production?
- How do you register a third-party agent so it isn't a shadow agent in the M365 admin center?
- Can a SPIFFE-issued JWT-SVID be exchanged at Entra? What does the FIC configuration look like?
11. References
- Integrate third-party agents (Bedrock, n8n)
- Entra ID Auth SDK (sidecar) reference
- Secure an Amazon Bedrock agent with Microsoft Entra Agent ID
- Secure an n8n agent with Microsoft Entra Agent ID
- Vendor-neutral Lesson 2.2 — Workload identity (SPIFFE, IRSA, WIF, Azure WI).
- Vendor-neutral Lesson 3.1 — Section 4 (Token Exchange RFC 8693) and JWT Bearer Grant (RFC 7523).
Sign in to save your progress and earn badges.