Skip to content

Client Auth: Workload Identity Federation (SEP-1933) #223

@PieterKas

Description

@PieterKas

Overview

Add conformance tests for MCP clients that authenticate using Workload
Identity Federation as described in SEP-1933
(modelcontextprotocol/modelcontextprotocol#1933).

This mechanism allows autonomous workloads (e.g. running on Kubernetes,
SPIFFE/SPIRE, or cloud runtimes) to exchange a platform-issued JWT for an
MCP access token, using the JWT Bearer grant defined in RFC 7523
(https://www.rfc-editor.org/rfc/rfc7523).

No client registration is required. Trust is established at the issuer
level — the authorization server maintains a trusted issuer allowlist and
resolves signing keys dynamically via OIDC Discovery. Key resolution is
an authorization server responsibility and is out of scope for client
conformance testing.

Specification References

Scope

This issue covers client conformance only. From the client's
perspective, it receives a pre-issued JWT assertion (via scenario context)
and must present it correctly to the token endpoint. The test authorization
server accepts the assertion at face value and issues an access token —
testing the authorization server's ability to perform OIDC Discovery, key
validation, and enforcing specific policies is explicitly out of scope
and belongs in authorization server conformance tests.

From the MCP server (resource server) perspective, the grant type used to
obtain an access token does not affect how the resource server processes
requests — no changes to createServer are required.

Changes Required

1. createAuthServer updates

  • Accept urn:ietf:params:oauth:grant-type:jwt-bearer at the token
    endpoint
  • Advertise the grant type in grant_types_supported on the
    /.well-known/oauth-authorization-server metadata endpoint
  • Support configurable rejection rules (e.g. rejectedSubjects,
    rejectedJtis, rejectedScopes, rejectGrantType) so the scenario
    can exercise specific error responses without implementing real policy
    logic
  • Record conformance checks on how the client formats its token request

2. JWT assertion helper (new)

Add helpers/createWorkloadJwt.ts — a parameterised helper that
generates a pre-signed JWT assertion for passing to the client under test
via scenario context. Accepts parameters including subject, audience,
exp, and jti so that each check can produce the specific JWT shape
it needs without requiring a separate signing mechanism.

3. New scenario: wif-jwt-bearer.ts

A single scenario consolidating all WIF client checks, following the
"fewer scenarios, more checks" principle from AGENTS.md. The scenario
starts the test authorization server and test MCP server once, then
exercises the full range of client behaviours across multiple checks
within the same scenario run.

The positive check exercises both the test authorization server (token
request format, grant type) and the test MCP server (bearer token
presented correctly). All negative checks terminate at the token endpoint
and do not reach the test MCP server.

4. Scenario registration

Register in src/scenarios/client/auth/index.ts. Tier placement
(required vs. extension) should follow SEP-1933's final status in the
spec process.

Checks to Cover

Positive (happy path)

  • Client correctly POSTs to token endpoint with
    grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer and
    assertion=<jwt>
  • Client uses the issued access token to successfully call the MCP
    server

Negative — all errors are non-recoverable without operator intervention;
client must fail clearly, fail fast, and not silently degrade

  • invalid_grant — expired exp: client does not retry
  • invalid_grant — wrong aud: client does not retry
  • invalid_grant — unauthorised sub: client does not retry
  • invalid_grant — replayed jti: client does not retry
  • invalid_scope — valid JWT but scope not accepted: client does not
    retry, surfaces scope-specific error distinct from JWT failure
  • invalid_request — client does not retry, does not hang, surfaces
    actionable error signalling misconfiguration
  • unauthorized_client — client does not retry, does not silently
    fall back to another grant type, surfaces error for operator
    intervention

Acceptance Criteria

  • createAuthServer accepts and records checks for the jwt-bearer
    grant type
  • Auth server metadata advertises
    urn:ietf:params:oauth:grant-type:jwt-bearer
  • createWorkloadJwt helper accepts parameters for subject,
    audience, exp, and jti — no separate signing mechanism
    needed per check
  • JWT assertion is generated server-side and passed to the client
    via context — client never generates its own assertion
  • Positive checks pass for a conformant client, verified against
    both the test authorization server and test MCP server
  • Negative checks verify the client correctly classifies each error
    as non-recoverable and does not retry, hang, silently fail, or
    fall back to a less secure method
  • Scenario registered in auth/index.ts
  • SEP-1933 reference implementation requirement satisfied
  • Scenario tested against at least one real SDK implementation
    before PR submission

Out of Scope

  • OIDC Discovery, signing key resolution, and enforcing specific policies
    (authorization server conformance concerns)
  • Client registration / DCR (not required for WIF)
  • Refresh token behaviour vs. always presenting fresh workload JWTs
    (open question in SEP-1933, deferred to follow-on)
  • Multi-tenant isolation tests (deferred to follow-on issue)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions