RFC compliance

TL;DR — AuthPlane implements the subset of each RFC needed for MCP authorization: not less, not more. Every deviation is opt-in, documented, and tied to an ADR. Legacy grants (implicit, ROPC) are intentionally omitted per OAuth 2.1 security guidance. This page is the compliance statement — one row per RFC with what’s covered, what’s not, and why.

Design philosophy

Every RFC below is implemented to the letter unless the “Deviation” column says otherwise. Where an RFC offers multiple approaches (token formats, client auth methods), AuthPlane picks the secure-by-default option. The full test matrix — one test per RFC section — is in authserver/wiki/COMPLIANCE_TEST_MATRIX.md.

Core OAuth

RFC 6749 — OAuth 2.0 Authorization Framework

Implemented sections: §4.1 (Authorization Code Grant), §4.4 (Client Credentials), §3.3 (Scope), §5.2 (Error responses).

Coverage:

  • Authorization Code grant with mandatory PKCE (RFC 7636)
  • Client Credentials grant (RFC 6749 §4.4) — see below
  • Scope validation against registered scopes
  • Client authentication: none, client_secret_basic, client_secret_post
  • Error responses per §5.2

Configurable default-scope handling (ADR-012): when oauth.require_scope: false, missing scope in authorize requests defaults to the resource’s registered scopes. RFC 6749 §3.3 explicitly allows this: “If the client omits the scope parameter … the authorization server MUST either process the request using a pre-defined default value or fail the request indicating an invalid scope.” We ship the fail branch by default (spec-strict); the toggle switches on the pre-defined default branch that the RFC also permits.

Not implemented: Implicit grant (§4.2), Resource Owner Password Credentials (§4.3) — OAuth 2.1 removes both from the spec.

RFC 6749 §4.4 — Client Credentials

Machine-to-machine token issuance. Requires client_secret_post or client_secret_basic. Supports scope validation and resource audience binding.

Configuration: client_credentials.enabled: true (disabled by default). Only confidential clients with client_credentials in their grant_types.

Token format: RFC 9068 JWT with typ: at+jwt, sub = client_id (no user), aud bound to resource when set. Introspection + revocation supported.

RFC 7636 — PKCE

S256 only. plain method rejected. Missing code_challenge rejected.

RFC 9700 — OAuth 2.0 Security BCP

Coverage: PKCE required, exact redirect-URI matching, refresh-token rotation with reuse detection, no implicit grant, DPoP available.

Token format

RFC 9068 — JWT Profile for OAuth 2.0 Access Tokens

Access tokens are JWTs with typ: at+jwt, standard claims (iss, sub, aud, exp, iat, jti, client_id, scope).

RFC 7517 — JSON Web Key (JWK)

JWKS endpoint at /.well-known/jwks.json. Supports ES256 (EC P-256) and RS256 key types.

Discovery

RFC 8414 — OAuth 2.0 Authorization Server Metadata

Full AS metadata at /.well-known/oauth-authorization-server. Fields returned:

  • issuer, authorization_endpoint, token_endpoint, registration_endpoint, revocation_endpoint
  • introspection_endpoint (when introspection enabled)
  • jwks_uri
  • response_types_supported, grant_types_supported, token_endpoint_auth_methods_supported
  • code_challenge_methods_supported: ["S256"]
  • scopes_supported (aggregated from all resources’ scopes)
  • resource_indicators_supported: true
  • dpop_signing_alg_values_supported (when DPoP enabled)
  • authplane_agent_identity_supported: true (AuthPlane extension)

RFC 9728 — OAuth 2.0 Protected Resource Metadata

Served by the resource server (i.e., your MCP server via the SDK) at /.well-known/oauth-protected-resource. MCP clients use this to discover the authorization server. AuthPlane’s SDKs generate and serve this automatically.

Client registration

RFC 7591 — Dynamic Client Registration

Three modes:

  • open — anyone can register (dev only)
  • approved_redirects — anyone can register, redirect URI must match dcr.approved_redirects patterns
  • admin_only — pre-registration required via Admin API/CLI

Fields supported: redirect_uris, client_name, token_endpoint_auth_method, grant_types, scope, and AuthPlane extensions for agent identity.

draft-ietf-oauth-client-id-metadata-document — CIMD

When client_id is a URL, AuthPlane fetches the metadata document at that URL, validates fields, and uses it for registration. Configurable via cimd.require_https (default true), cimd.cache_ttl, cimd.fetch_timeout.

Resource indicators

RFC 8707 — Resource Indicators for OAuth 2.0

The resource parameter in /oauth/authorize and /oauth/token binds tokens to a specific resource server via the aud claim.

Strict exact-string matching — trailing slashes matter. See Configuration: Resources for the URI-mismatch trap.

Token lifecycle

RFC 7009 — Token Revocation

Revocation endpoint at /oauth/revoke. Accepts both access tokens and refresh tokens. Returns 200 on success and for invalid/unknown tokens per §2.2; error responses (unsupported_token_type, client-auth failure, 503) follow §2.2.1.

RFC 7662 — Token Introspection

Introspection endpoint at /oauth/introspect. Accepts access tokens and machine tokens. Requires client authentication (client_secret_post or client_secret_basic) for confidential clients.

Refresh token rotation

Refresh tokens rotate on every use — a new token is issued, the old one consumed. Reuse detection — a second use of a consumed refresh token revokes the entire token family (RFC 9700 §4.14).

Error format

RFC 9457 — Problem Details for HTTP APIs

Error responses include both OAuth error fields (error, error_description) and Problem Details fields (type, title, detail, status). Content-Type: application/problem+json.

Full error catalog in Reference: Errors.

Proof of possession

RFC 9449 — OAuth 2.0 Demonstrating Proof of Possession (DPoP)

Full support including proof validation, token binding, and server nonces.

Coverage:

  • DPoP proof JWT validation (§4.3): typ, alg, jwk, htm, htu, iat, jti, nonce
  • Supported algorithms: ES256, RS256, PS256
  • Algorithm restriction: alg:none and all symmetric algorithms rejected
  • Private key in jwk header rejected
  • htu comparison strips query string (scheme + authority + path; authority includes non-default port; compared after RFC 3986 normalization)
  • JKT computation per RFC 7638 (JWK Thumbprint) — base64url-encoded SHA-256
  • Token binding via cnf.jkt claim in access tokens
  • DPoP-bound token type: token_type: DPoP
  • Server-issued nonces (DPoP-Nonce response header) with configurable TTL
  • JTI replay prevention with database-backed store + background purge
  • ath (access token hash) validation on resource requests
  • Backward compatible: no DPoP proof → standard Bearer token
  • Introspection returns cnf.jkt for DPoP-bound tokens
  • AS metadata advertises dpop_signing_alg_values_supported

Configuration: dpop.enabled: true (disabled by default).

Token exchange

RFC 8693 — OAuth 2.0 Token Exchange

Full support for impersonation and delegation flows.

Coverage:

  • grant_type=urn:ietf:params:oauth:grant-type:token-exchange
  • Subject token validation: signature, issuer, expiry, revocation
  • Subject token types: access_token, jwt URNs
  • Impersonation: no actor token, no act claim, sub preserved
  • Delegation: actor token present, nested act claim per §4.1
  • Multi-hop delegation: correct chain nesting
  • Scope narrowing: requested scope must be subset of subject-token scope
  • Configurable chain depth limit (1-10, default 5)
  • Per-resource policy enforcement (policy.exchange.allowed_client_ids)
  • Self-exchange guarded by token_exchange.allow_self_exchange
  • DPoP binding propagation on exchanged tokens
  • AS metadata: grant_types_supported includes the URN

Configuration: token_exchange.enabled: true (disabled by default).

AuthPlane extensions

JWT Bearer + XAA (RFC 7523 + ID-JAG assertion profile)

Enterprise-Managed Authorization via JWT Bearer grant with the ID-JAG assertion format — an emerging IETF/OIDF draft referenced by the MCP Authorization spec (2025-11-25).

Coverage:

  • grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer
  • ID-JAG assertion validation: signature, issuer, audience, expiry, type header (oauth-id-jag+jwt)
  • Trusted IdP registry with JWKS discovery + caching (SSRF-protected)
  • Policy engine: IdP + client_id + scope + resource constraint evaluation
  • Subject mapping: auto_map (federated subject) and strict (explicit mapping required) modes
  • Replay prevention: assertion JTI single-use enforcement + auto-purge
  • Scope intersection: policy scopes narrow the issued token’s scope
  • Resource binding: resource parameter flows to token aud claim
  • DPoP binding: XAA tokens support cnf.jkt
  • Machine token storage for revocation/introspection
  • AS metadata: grant_types_supported includes jwt-bearer URN when enabled

Configuration: xaa.enabled: true (disabled by default). YAML-only at v0.1.x — no env-var overrides.

No deviations from RFC 7523 §2.1 (JWT assertion profile).

Agent identity claims

AuthPlane-specific JWT extensions:

  • agent_id — set to client_id when issuing client has is_agent=true
  • agent_chain — ordered list built from delegation act chain, capped at 8
  • Agent registration via DCR (agent: true, agent_description max 255 chars)
  • Optional JWKS agent listing (agents.enable_jwks_listing: true)
  • AS metadata advertises authplane_agent_identity_supported: true

Not standardized. See Concepts: Agent identity.

MCP-specific

MCP Authorization Specification (2025-11-25)

Implemented: Full discovery flow (PRM → AS Metadata → DCR → Authorize → Token), CIMD support, resource indicators, DPoP-bound tokens.

Tested against: Claude Code, Claude Desktop, MCP Inspector. Cursor and VS Code interop is documented for guidance but not yet in the regression set — status Pending in the compatibility matrix.

OAuth 2.1 itself remains an active IETF draft (draft-ietf-oauth-v2-1) rather than a published RFC — AuthPlane tracks the latest revision.

Summary matrix

RFCCoverageDeviationConfiguration
RFC 6749§4.1, §4.4, §3.3, §5.2ADR-012 (opt-in)Always on
RFC 7636 (PKCE)S256 onlyplain rejected (per BCP)Always on
RFC 7009 (Revocation)FullNoneAlways on
RFC 7517 (JWK)ES256, RS256NoneAlways on
RFC 7523 (JWT Bearer)Full assertion profileNonexaa.enabled
RFC 7591 (DCR)FullNonedcr.mode
RFC 7662 (Introspection)FullNoneAlways on
RFC 8414 (AS Metadata)FullNoneAlways on
RFC 8693 (Token Exchange)FullNonetoken_exchange.enabled
RFC 8707 (Resource Indicators)Full (exact match)NoneAlways on
RFC 9068 (JWT AT)FullNoneAlways on
RFC 9449 (DPoP)FullNonedpop.enabled
RFC 9457 (Problem Details)FullNoneAlways on
RFC 9700 (OAuth BCP)FullNoneAlways on
RFC 9728 (PRM)Full (via SDK)NoneAlways on
CIMD draftFullNonecimd.enabled