Third-party agents and federation

Bring Bedrock, n8n, Ollama, and SPIFFE-based agents under one Entra governance plane.

🟦 Module 10 13 min read Not started

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

  1. Choose between sidecar and WIF for a given third-party agent.
  2. Deploy the Entra ID Auth SDK sidecar alongside a Bedrock, n8n, or Ollama agent.
  3. Configure Federated Identity Credentials on a blueprint to trust AWS STS / GCP WIF / SPIFFE.
  4. Migrate an existing SPIFFE-based agent to Entra Agent ID without losing local mTLS.
  5. Combine Microsoft-native governance with your existing multi-cloud identity fabric.

1. Sidecar vs Workload Identity Federation

Two patterns; pick per agent workload.

AspectSidecarWIF (Workload Identity Federation)
Where credentials liveSidecar containerExternal identity provider (AWS STS / GCP / SPIFFE)
Extra container?YesNo
Agent code changesMinimal — HTTP call to localhost:7000Larger — agent must fetch and exchange the federated token
Best forContainerised agents on Docker / K8s (Bedrock, LangChain, n8n, Ollama)Native cloud agents already using AWS STS / GCP WIF
Local dev friendlinessExcellent — Docker ComposePoor — needs federation set up first
Language-agnosticYesDepends on client library
Recommended defaultYes (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 endpoint

The 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):

python
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:

yaml
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 (AssumeRoleWithWebIdentity returns 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.
  • 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/.default
  • Entra 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.com with 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:

  1. Create the Entra blueprint for its class. Add an FIC trusting your SPIRE trust domain, subject = the agent's SPIFFE ID.
  2. Create an agent identity under that blueprint. Populate sponsors, owners, custom security attributes.
  3. Deploy the sidecar (or add a Python line to your agent to do the JWT-bearer exchange itself).
  4. Grant permissions on the blueprint principal for the Graph / Azure resources the agent will touch.
  5. Update CI/CD to also register the agent in the M365 admin center registry (so it's not shadow).
  6. 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

PlatformDeployment pattern
Azure Container AppsAdd sidecar as a second container in the app definition; both reachable at localhost
AKS / KubernetesSame pod as the agent container, localhost:7000
AWS ECS FargateTask definition with two containers; agent + sidecar
AWS EKSSame pod as agent (K8s pattern)
GCP Cloud RunMulti-container Cloud Run service (2024+)
GKESame pod as agent
On-prem K8sSame pod, standard sidecar pattern
Docker Compose (dev)Two services, shared network
VMDocker 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

SymptomLikely causeFix
Agent can't reach sidecarNetwork / DNS / sidecar not runningVerify sidecar container is up; check port 7000; check compose network name
Sidecar fails to acquire tokenBlueprint auth failedVerify blueprint client id + credential; check tenant id; check FIC issuer / subject match
Sidecar 401Missing FIC or wrong subjectRecheck FIC issuer + subject; check MI principal id matches subject
Token 401 at GraphMissing permission or wrong scopeVerify blueprint principal has consented permission; ensure .default or explicit scope matches
Token issued but resource 403Permission blocked by Entra (Lesson 10.4)Confirm not in blocked-permission list; check inheritable permissions actually cascaded
CA blockingReport-only CA policy escalated to onCheck CA sign-in logs for policyId; adjust filter
Sidecar container OOMCache growthConfigure token cache TTL; scale RAM
Bedrock agent works locally, fails in ECSECS task role doesn't have the STS OIDC configIf using WIF pattern, verify task role assumes the identity that matches the FIC subject

8. Common pitfalls

  1. Trying to use client secrets from a Bedrock agent — works, but wildly insecure and fails ISO 42001. Use FIC → AWS STS.
  2. Two sidecars in one pod — pick one; multiple sidecars produce inconsistent caching and confuse observability.
  3. Exposing the sidecar port publicly — instant credential exfiltration risk. Bind to localhost only.
  4. Not creating an agent identity per environment — one blueprint, one dev + one prod identity minimum.
  5. Skipping the M365 admin registry entry — your third-party agent becomes a shadow agent (Lesson 10.7). Register it.
  6. SPIFFE trust domain not exposed via OIDC — SPIRE 1.5+ has an OIDC discovery URL; older SPIRE needs an upgrade or a middleware.
  7. FIC configured for refs/heads/main when the CI job runs on refs/heads/release/* — exchange fails with cryptic "AADSTS70021: No matching federated identity record found". Match subject exactly.
  8. 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)

  1. Clone the Entra Auth SDK sidecar sample.
  2. Create a test blueprint + agent identity (Lesson 10.2).
  3. Author docker-compose.yml with three services: Ollama, sidecar, and a small LangChain agent.
  4. 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)

  1. Create an ECS task definition with your Bedrock invocation container + the sidecar container.
  2. Deploy to Fargate. Ensure the ECS task role has permission to fetch the blueprint's certificate from AWS Secrets Manager.
  3. Register the sidecar as the agent's identity provider.
  4. Have the Bedrock agent invoke Graph via the sidecar.

Part C — WIF from AWS STS

  1. Configure an FIC on the blueprint trusting your AWS OIDC issuer + IAM role ARN.
  2. Modify the agent to fetch its AWS STS OIDC token and exchange directly at Entra.
  3. Remove the sidecar entirely from part B; verify the agent still works.
  4. Compare deployment complexity + audit trail between the two patterns.

Part D — SPIFFE (bonus)

  1. Deploy SPIRE (per Lesson 2.2).
  2. Enable SPIRE's OIDC discovery endpoint.
  3. Add an FIC on the blueprint trusting the SPIRE trust domain.
  4. Agent fetches a JWT-SVID and exchanges at Entra.
  5. Verify the same SPIFFE identity is now valid for mTLS to your internal APIs and for Entra Graph.

10. Self-check

  1. When should you pick sidecar over WIF? Vice versa.
  2. What are the two conditions for a Federated Identity Credential to match at exchange time?
  3. Which port does the sidecar bind to by default? Where should it be reachable from?
  4. Why is Microsoft's own guidance to avoid client secrets in production?
  5. How do you register a third-party agent so it isn't a shadow agent in the M365 admin center?
  6. Can a SPIFFE-issued JWT-SVID be exchanged at Entra? What does the FIC configuration look like?

11. References

Sign in to save your progress and earn badges.