Skip to content

Phase 6: dedicated auth worker — RFC 7591 DCR + RFC 8414 metadata + JWKS rotation, all via iii primitives #46

@rohitg00

Description

@rohitg00

Phase 6: dedicated auth worker — RFC 7591 DCR + RFC 8414 metadata + JWKS rotation, all via iii primitives

Tracks an additive follow-up to the v0.4.0 overhaul (#45). Targets v0.5.0.

Why

Phase 1 of #45 delegates exposure to iii-worker-manager RBAC via auth_function_id. That's the hook. It is currently empty — every deploy must write its own auth function from scratch.

The MCP + A2A specs both lean on the same OAuth 2.1 primitives:

  • RFC 7591 — Dynamic Client Registration. Clients onboard at runtime via POST /register. Without it, every Claude Desktop / Cursor / IDE that hits a federated MCP needs an admin to register manually at each server. Doesn't scale past ~5 servers. Anthropic's MCP marketplace flow assumes DCR.
  • RFC 8414 — Authorization Server Metadata. /.well-known/oauth-authorization-server. Tells clients which scopes / grant types / PKCE methods the IdP supports. We already cover the resource-server side (RFC 9728); 8414 is the auth-server side. Both required for full client discovery.
  • JWKS rotation, token introspection, IdP capability matrix (Okta / Auth0 / Entra / Google / Keycloak / Ping / ForgeRock). Builders pick IdP first, write code second. Without this content next to the worker, they wire Entra-backed MCP, hit the 7591 wall in prod, rip out + redo.

Current state: each MCP/A2A deploy reimplements this stack, usually as FastAPI middleware.

Iron rule: everything through iii primitives. No FastAPI middleware bolted on the side. Auth becomes a worker the same way every other capability is a worker.

Surface

New crate iii-hq/workers/auth/. Sibling shape to introspect/ and llm-router/ (~1500-2000 LOC).

Functions registered:

Function ID Trigger Purpose RFC
auth::validate (called via iii.trigger by iii-worker-manager) Token introspection — returns AuthResult { allowed_functions, forbidden_functions, context }
auth::server_metadata HTTP GET /.well-known/oauth-authorization-server Authorization server discovery doc 8414
auth::resource_metadata HTTP GET /.well-known/oauth-protected-resource Resource server discovery doc 9728
auth::register HTTP POST /register Dynamic Client Registration 7591
auth::jwks HTTP GET /.well-known/jwks.json Public key set
auth::jwks_rotate cron (e.g. daily) Generate new keypair, retire old, update state::set
auth::token HTTP POST /token Token issuance + refresh + introspection 6749 / 7662

State scopes:

  • auth:clients — registered clients, indexed by client_id. Survives worker restart.
  • auth:jwks — current + previous keypairs (rotation overlap window).
  • auth:tokens — refresh tokens + revocation list.

CLI flags:

--engine-url <URL>
--issuer <URL>           # public base URL advertised in metadata
--idp-mode <KIND>        # local | bridge:keycloak | bridge:okta | bridge:auth0 | bridge:entra
                         # local issues + verifies own JWTs; bridge: federates to external IdP
--rotation-cron <EXPR>   # default "0 3 * * *" (daily 3am UTC)
--debug

Wiring into mcp + a2a

No code changes in the protocol workers. The deploy authors config.yaml:

workers:
  - name: iii-worker-manager
    config:
      rbac:
        auth_function_id: auth::validate
        expose_functions:
          - metadata:
              public: true
  - name: iii-auth
    config:
      issuer: https://api.acme.dev
      idp-mode: bridge:keycloak
  - name: iii-mcp     # served as POST /mcp
  - name: iii-a2a     # served as GET /.well-known/agent-card.json + POST /a2a

Now every tools/call (MCP) and message/send (A2A) flows through auth::validate. JWKS rotation is automatic. DCR endpoint is live. Auth-server metadata is discoverable. None of this lives in the mcp or a2a crate.

IdP capability matrix

Document in auth/README.md next to the worker. Initial roster (from Posta's research):

IdP RFC 7591 DCR RFC 8414 metadata PKCE Notes
Keycloak yes yes required reference IdP for bridge:keycloak; full demo in README
Okta yes yes required bridge config straightforward
Auth0 yes yes required bridge config straightforward
Entra ID no yes required DCR not supported — clients must be pre-registered manually
Google no yes required DCR not supported
Ping yes yes required bridge config
ForgeRock yes yes required bridge config

Builders read this BEFORE picking. Saves the rip-and-redo cycle.

Differentiation vs incumbents

Posta ships FastAPI middleware. iii ships a worker that any protocol bridge consumes. mcp/a2a stay narrow protocol transports — auth doesn't pollute their crates. JWKS rotation is a cron trigger, not a thread you have to remember to start. DCR is a function any worker can override (e.g. inject domain validation) by registering auth::register with higher priority. That last move is impossible in middleware-shaped frameworks.

Out of scope

  • mTLS / client cert flows.
  • SAML / WS-Federation.
  • Per-tenant key isolation (single-tenant for v0.5.0; multi-tenant tracked separately).
  • Token issuance for non-OAuth flows.

Sequencing

Lands as v0.5.0 after #45 ships v0.4.0. No dependency on Phase 2c (Streamable HTTP SSE) — auth works against the existing Authorization header pathway today.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions