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 canonical per-RFC compliance statement, with the conformance evidence behind it, is authserver/docs/reference/compliance.md; this page tracks it.
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 — on by default since v0.2.0, disabled by default through v0.1.x. 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_endpointintrospection_endpoint(whenintrospectionenabled)jwks_uriresponse_types_supported,grant_types_supported,token_endpoint_auth_methods_supportedcode_challenge_methods_supported: ["S256"]scopes_supported(aggregated from all resources’ scopes)resource_indicators_supported: trueclient_id_metadata_document_supported: true(CIMD)authorization_response_iss_parameter_supported: true— always emitted, never omitted (RFC 9207, since v0.2.0)authorization_grant_profiles_supported— listsurn:ietf:params:oauth:grant-profile:id-jag(since v0.2.0)dpop_signing_alg_values_supported(when DPoP enabled)authplane_agent_identity_supported: true(AuthPlane extension)
The same document is aliased at /.well-known/openid-configuration.
RFC 9207 — Authorization Server Issuer Identification
Since v0.2.0, every authorization response — success and error alike — carries iss set to the AS issuer identifier (§2), and AS metadata emits authorization_response_iss_parameter_supported: true explicitly rather than omitting it, so a client applying §2.4’s strict policy can reject a response that arrives without iss. The value is written verbatim; clients compare it byte for byte. The MCP 2026-07-28 specification lists this as SHOULD, announced to become MUST.
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/<resource-path> (§3.1 path-insertion form). MCP clients use this to discover the authorization server. AuthPlane’s SDKs generate and serve this automatically.
Since v0.2.0 the AS also serves the document for every registered Resource — GET /.well-known/oauth-protected-resource and /.well-known/oauth-protected-resource/{ref} — so a resource server that cannot host well-known paths itself still has a conformant document to point its WWW-Authenticate: resource_metadata at.
Client registration
RFC 7591 — Dynamic Client Registration
Three modes:
open— anyone can register (dev only)approved_redirects— anyone can register, redirect URI must matchdcr.approved_redirectspatternsadmin_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:noneand all symmetric algorithms rejected - Private key in
jwkheader rejected htucomparison 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.jktclaim in access tokens - DPoP-bound token type:
token_type: DPoP - Server-issued nonces (
DPoP-Nonceresponse 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.jktfor DPoP-bound tokens - AS metadata advertises
dpop_signing_alg_values_supported
Configuration: dpop.enabled — on by default since v0.2.0, disabled by default through v0.1.x.
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,jwtURNs - Impersonation: no actor token, no
actclaim,subpreserved - Delegation: actor token present, nested
actclaim 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_supportedincludes the URN
Configuration: token_exchange.enabled — on by default since v0.2.0, disabled by default through v0.1.x.
AuthPlane extensions
JWT Bearer + XAA (RFC 7523 + ID-JAG assertion profile)
Enterprise-Managed Authorization via JWT Bearer grant with the ID-JAG assertion format — the stable Enterprise-Managed Authorization extension of the MCP Authorization spec (2026-07-28).
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) andstrict(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:
resourceparameter flows to tokenaudclaim - DPoP binding: XAA tokens support
cnf.jkt - Machine token storage for revocation/introspection
- AS metadata:
grant_types_supportedincludes thejwt-bearerURN, andauthorization_grant_profiles_supportedlistsurn:ietf:params:oauth:grant-profile:id-jag(since v0.2.0) — the field the extension tells clients to check
Configuration: xaa.enabled — on by default since v0.2.0 (the grant validates nothing until a trusted IdP is registered), disabled by default through v0.1.x, where the block is YAML-only. AUTHPLANE_XAA_* env vars exist since v0.2.0.
No deviations from RFC 7523 §2.1 (JWT assertion profile).
Agent identity claims
AuthPlane-specific JWT extensions:
agent_id— set toclient_idwhen issuing client hasis_agent=trueagent_chain— ordered list built from delegationactchain, capped at 8- Agent registration via DCR (
agent: true,agent_descriptionmax 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 (2026-07-28)
Implemented: the full authorization-server side of the 2026-07-28 revision, and the stable Enterprise-Managed Authorization extension.
Out of scope on the AS side: the non-auth bulk of 2026-07-28 (stateless core, server/discover, subscriptions, cache hints) is MCP server and SDK territory; minting ID-JAGs is the enterprise IdP’s role in that three-party model; the OAuth Client Credentials ext-auth extension is still draft and not committed to.
Tested against (v0.2.0, by hand, full flow through tools/call): Claude Desktop (CIMD, hosted callback), Claude Code (CIMD, loopback callback), MCP Inspector (DCR). Per-client detail in mcp-client-compatibility.md.
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
Related
- Reference: Configuration — every
*.enabledflag from the table above - Reference: Errors — RFC 9457 problem+json shape +
WWW-Authenticatecatalog - Reference: Public API — the endpoints each RFC lives on
- Concepts: Grants & flows — RFC-by-RFC mapping to actual flows
- Compliance statement —
authserver/docs/reference/compliance.md— the canonical per-RFC statement this page tracks, with the conformance evidence