# Do AI Agents Need SPIFFE or OAuth?

> SPIFFE proves which agent process is calling; OAuth scopes what it may do and for whom. When each is enough, and when production agents need both.

- Canonical URL: https://webofmike.com/spiffe-vs-oauth-for-ai-agents/
- Author: Mike Moore (https://webofmike.com/about/)
- Published: 2026-10-05
- Last modified: 2026-10-05
- Tags: AI Gateways, Security, Platform Engineering, Generative AI, MCP
- Cite as: Mike Moore, "Do AI Agents Need SPIFFE or OAuth?", Web of Mike (webofmike.com), 2026-10-05. https://webofmike.com/spiffe-vs-oauth-for-ai-agents/


They answer different questions. **SPIFFE** proves *which process* is calling, without a provisioned secret. **OAuth** scopes *what it may do* and, when a human is in the loop, *for whom*. Most production agent platforms need both, joined by assertion or token exchange. Neither is a winner.

*Disclosure: I work at Solo.io, which created agentgateway and sells Solo Enterprise for agentgateway. The demos I cite are public repositories.*

This page is the when-each. The how-to is [SPIFFE identity for AI agents](/spiffe-identity-for-ai-agents/), where I ran agentgateway + SPIRE until `agent-alpha` got HTTP 200 and `agent-beta` got HTTP 403. I am not rewriting that demo. Architecture context is [what an agentic mesh is](/what-is-an-agentic-mesh/). Identity as an evaluate row is [how to choose an AI agent gateway](/how-to-choose-an-ai-agent-gateway/).

## The short answer

**SPIFFE/SPIRE** gives an agent an attested, short-lived workload identity (an SVID) so the platform can prove which process is calling without a long-lived secret at rest. **OAuth** gives a scoped, audience-restricted access token, and a delegation story (on-behalf-of) when a user is in the loop. OAuth alone does not attest the runtime. SPIFFE alone does not carry permissions or a user.

Enough alone only in the narrow cases in the table below. Production platforms usually run both.

Lin Sun's CNCF post [Agent Auth: a lawyer's day in court](https://www.cncf.io/blog/2026/06/23/agent-auth-a-lawyers-day-in-court/) is the metaphor I would paste into the ADR: the lawyer is not the client. The agent is not the user. You need agent identity, principal identity, and a delegation artifact, then policy on top.

## Two different questions

Stop treating them as rivals. I already wrote the sentence on the demo page: **identity is not authorization**. A verified name is not a permission.

### What a SPIFFE ID gives you (and what it refuses to give)

SPIRE attests the calling process (in my demo, a docker label; on Kubernetes, namespace and service account) and issues a short-lived X.509 or JWT SVID from the Workload API. There is no key file to mount. The certificate lives minutes and rotates in place.

What you get: a verified name (`spiffe://example.org/ns/demo/sa/agent-alpha`), mTLS or a JWT-SVID, and a bootstrap that does not start with a provisioned secret.

What you do not get: a permission model, scopes, audiences for third-party APIs, or a human `sub`. The demo's `agent-beta` holds a perfectly valid SVID and still gets 403, because CEL allows only `agent-alpha`. That is the point.

### What an OAuth token gives you (and what it refuses to give)

An access token is a permission envelope: scopes, audience, expiry, and (with OBO) a user. A resource can decide what this caller may do, and for whom.

What you do not get: platform attestation that *this process* is the agent. A client secret or a private key the agent holds is possession of a credential, not workload identity. A fleet that shares one `client_id` audits a credential, not a workload. Stolen bearer tokens are ambient authority until they expire.

MCP's shipped enterprise auth authenticates the employee through an IdP, not the unattended agent. I walked that in [MCP agent identity](/mcp-agent-identity-gap/).

## When each is enough

| Situation | Reach for | Because |
| --- | --- | --- |
| In-cluster agent → gateway → model, one trust domain, no human OBO | SPIFFE (or mesh equivalent) | Policy can be an ACL on `source.spiffeId`. I ran this. |
| Agent acting for a logged-in human | Both | You need the agent principal *and* an OBO / subject token. |
| Third-party SaaS APIs that only speak OAuth client auth | OAuth, plus SPIFFE in front if you can attest the caller | The SaaS will not verify your SVID. The gateway still should. |
| CI, laptop, or serverless with no SPIRE | OAuth or platform OIDC | You cannot attest the workload the SPIFFE way yet. Say so. |
| Multi-hop agent → agent | Both, joined by exchange | Each hop needs a new audience. Do not forward the same bearer. |
| MCP tools with different risk profiles | Both | A SPIFFE name is not a tool allowlist; a user token is not a workload. |

### SPIFFE-only (honest scope)

Same trust domain you control end to end. Policy is an ACL on the SPIFFE ID at the gateway. No human OBO required.

That is the [SPIFFE demo](/spiffe-identity-for-ai-agents/). `verify.sh` asserts the claims rather than narrating them:

```
verifying...
  ok    no certificate or key files in the repo
  ok    gateway config contains no cert or key path
  ok    agent-alpha gets HTTP 200
  ok    agent-alpha holds no provider credential
  ok    upstream authenticated the gateway's SVID
  ok    agent-beta gets HTTP 403
  ok    gateway echoes the verified SPIFFE ID

7 passed, 0 failed
```

The agent reports `provider credentials I hold: {'env': 'none', 'key_files': 'none'}` and still gets a 200, because the gateway attached the upstream key. The upstream saw `sa/agentgateway`, not `sa/agent-alpha`. Identity terminated at the hop. That is SPIFFE-only *plus* secretless injection, which is still not a permission envelope for a third-party API.

### OAuth-only (honest scope)

No platform attestation available (developer laptop, customer-managed runtime), or you only call APIs that only accept OAuth client authentication. agentgateway's MCP auth (OAuth per the MCP auth spec) is a fine starting point. Disclose the limits: shared secrets, fleet-wide `client_id`, stolen bearer, audit that names a credential not a workload.

Do not pretend a virtual key the agent holds is SPIFFE.

### Both (the usual production answer)

SVID (or platform OIDC) authenticates the agent. An OAuth access token carries scopes, audience, and (when needed) the user. They join in two documented ways:

1. **SVID as client credential.** [draft-ietf-oauth-spiffe-client-auth-02](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-spiffe-client-auth-02), an OAuth working-group Internet-Draft (not yet an RFC), profiles RFC 7521 / RFC 7523 so a workload presents a JWT-SVID, WIT-SVID, or X.509-SVID as the client assertion. No static client secret.
2. **SVID as actor token.** Ping's [token exchange + workload identity](https://developer.pingidentity.com/blog/securing-agentic-workflows-with-token-exchange-and-workload-identity/) pattern uses RFC 8693: `subject_token` is the user, `actor_token` is the JWT-SVID, result is an audience-restricted access token for the next hop.

agentgateway can perform [RFC 8693 token exchange](https://agentgateway.dev/docs/kubernetes/latest/documentation/security/backend-authn/token-exchange/standard/) on the outbound path: incoming token becomes `subject_token`, the exchanged token is what the backend sees. That token-exchange page is Kubernetes docs. The [SPIFFE](/spiffe-identity-for-ai-agents/) and [secretless](/secretless-ai-agents/) demos ran on OSS v1.5.0 standalone. Pair inbound SPIFFE from the demo with outbound exchange when you are on the Kubernetes path.

Audience restriction is the OAuth-shaped control that sits next to workload identity. kagent v0.10 can send an RFC 8707 resource indicator on the exchange; both env vars default to empty ([audience-bound agent tokens](/kagent-audience-bound-agent-tokens/)).

## How they join on an agent path

One hop list. No new repo.

1. The agent fetches an SVID from the Workload API (no cert file).
2. It presents that SVID as a client assertion, or the gateway/STS takes it as `actor_token`.
3. The authorization server mints an access token for **one** audience, with the scopes that hop needs.
4. The gateway verifies SPIFFE on the wire, authorizes the tool or model, and (if the backend wants OAuth) forwards the exchanged token.
5. Provider keys stay at the gateway ([secretless](/secretless-ai-agents/)). MCP tool allowlists stay at the gateway ([federation](/multi-tenant-mcp-federation/), run on Solo Enterprise for agentgateway).

Do not pass bearer JWT-SVIDs down an agent chain. A JWT-SVID copied to agent B is onwards impersonation, not proof-of-possession. Mint a new, audience-restricted token per hop.

### Gateway as the enforcement hop

This is the [agentic mesh](/what-is-an-agentic-mesh/) picture in one sentence: verify SPIFFE on the wire, inject secretless provider creds, authorize tools. The gateway is where "both" becomes a path instead of an IdP slide. Score that path with the [evaluate-gateway](/how-to-choose-an-ai-agent-gateway/) identity row. I run it on agentgateway, standalone or on Kubernetes.

Identity on the intended path is not enough if agents CONNECT elsewhere. That failure is [egress bypasses](/agent-egress-control-bypasses/).

## Failure modes if you pick only one

**OAuth-only.** Shared client secrets, one `client_id` for the fleet, stolen bearer, audit without a workload name. A prompt file does not fix it ([AGENTS.md is not a security control](/agents-md-not-a-security-control/)).

**SPIFFE-only.** A name mistaken for permission (the demo's 403 is there so you do not do this). No human attribution when the agent acts for a user. Third-party APIs still want OAuth. Multi-tenant tool entitlements still need something that is not only `source.spiffeId`.

## Is SPIFFE overkill for a small team?

Often, yes, as step one.

Start with platform OIDC or standalone agentgateway and hard scopes. Add SPIRE when you have multi-team agents, a shared MCP plane, or a compliance need for attested workload identity. That is the same fork as the [evaluate-gateway](/how-to-choose-an-ai-agent-gateway/) identity criterion. You do not owe anyone a SPIRE DaemonSet on week one of a single-provider prototype.

## Where to go next on webofmike

- **How:** [SPIFFE identity for AI agents](/spiffe-identity-for-ai-agents/)
- **Secretless keys:** [Your AI agent should not hold the LLM API key](/secretless-ai-agents/)
- **MCP identity:** [MCP agent identity gap](/mcp-agent-identity-gap/)
- **Audience:** [kagent audience-bound agent tokens](/kagent-audience-bound-agent-tokens/)
- **Tenancy:** [Multi-tenant MCP federation](/multi-tenant-mcp-federation/) (Solo Enterprise)
- **Egress:** [Agent egress control bypasses](/agent-egress-control-bypasses/)
- **Definition / evaluation / plane:** [agentic mesh](/what-is-an-agentic-mesh/), [evaluate an AI agent gateway](/how-to-choose-an-ai-agent-gateway/), [how agentgateway's plane differs from an LLM proxy or a managed AI-ops plane](/agentgateway-vs-litellm-vs-portkey/)

External primaries:

- [Lin Sun / CNCF: Agent Auth](https://www.cncf.io/blog/2026/06/23/agent-auth-a-lawyers-day-in-court/)
- [IETF draft-ietf-oauth-spiffe-client-auth-02](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-spiffe-client-auth-02)
- [Ping: token exchange + workload identity](https://developer.pingidentity.com/blog/securing-agentic-workflows-with-token-exchange-and-workload-identity/)
- [agentgateway: RFC 8693 token exchange](https://agentgateway.dev/docs/kubernetes/latest/documentation/security/backend-authn/token-exchange/standard/)

*The OSS project is [agentgateway/agentgateway](https://github.com/agentgateway/agentgateway). This page is a when-each, not a claim that SPIFFE replaces OAuth.*

