GNAP vs OAuth 2.0: Is RFC 9635 Ready for Production?
GNAP (the Grant Negotiation and Authorization Protocol, RFC 9635, published October 2024 by Justin Richer and Fabien Imbault) is a new delegation protocol, not an OAuth 2.0 extension. Instead of a redirect with client_id and scope, the client POSTs a JSON document describing what access it wants, how the user can be reached and which key it will sign with. Production readiness is the open question: the protocol is a Proposed Standard, but I could not find evidence of a mainstream identity provider shipping it. For today's production work, the better investment is hardening OAuth with PKCE and token binding and DPoP. GNAP is worth understanding now because its ideas keep leaking into those specs.
We also walked through this comparison on video: GNAP vs OAuth 2.0: Is the Grant Negotiation Protocol Ready for Production.
What problem GNAP is solving
OAuth 2.0 (RFC 6749) was written around one scenario: a user in a browser approves a pre-registered web app. Everything since has been extensions bolted on: PKCE, device flow, PAR, RAR, DPoP, token exchange. RFC 9635 Appendix A lists the assumptions GNAP drops:
| Concern | OAuth 2.0 | GNAP (RFC 9635) |
|---|---|---|
| Who approves | Assumes the user at the client is the user at the AS | Allows various patterns; interaction "can use a browser, but doesn't have to" |
| Client identity | Pre-registered client_id known to the AS | Client instance presents a key; AS may accept an unknown key |
| Requesting access | scope strings (RAR adds structured objects) | Structured access objects, with plain strings as an optimization |
| Entry point | Different endpoints and grant types per flow | Every request starts with the same request to one endpoint |
| Token security | Bearer by default; DPoP/mTLS optional | Key proof is built in; bound tokens are the normal case |
| Refresh | Separate refresh tokens | Not needed when access tokens are rotated (Section 6.1) |
| Public clients | Distinct client type with weaker guarantees | No such notion; key material can always be sent |
If you have used Rich Authorization Requests or DPoP, much of this will feel familiar. GNAP makes them the baseline instead of optional add-ons.
A GNAP grant request, field by field
The client sends one POST with Content-Type: application/json to the AS grant endpoint. This is the non-normative example from RFC 9635 Section 2:
{ "access_token": { "access": [ { "type": "photo-api", "actions": ["read", "write", "dolphin"], "locations": ["https://server.example.net/", "https://resource.local/other"], "datatypes": ["metadata", "images"] }, "dolphin-metadata" ] }, "client": { "display": { "name": "My Client Display Name", "uri": "https://example.net/client" }, "key": { "proof": "httpsig", "jwk": { "kty": "RSA", "e": "AQAB", "kid": "xyz-1", "alg": "RS256", "n": "kOB5rR4Jv0GMeL...." } } }, "interact": { "start": ["redirect"], "finish": { "method": "redirect", "uri": "https://client.example.net/return/123455", "nonce": "LKLTI25DK82FX4T4QFZC" } }, "subject": { "sub_id_formats": ["iss_sub", "opaque"], "assertion_formats": ["id_token"] } } -
access_token.accessreplacesscope. The first element is a RAR-style object (type,actions,locations,datatypes);"dolphin-metadata"is a plain string reference, the scope-like shortcut. -
client.keyreplacesclient_id+ secret.proofnames the proofing method, andjwkcarries the public key. The registered proofing methods arehttpsig,mtls,jwsdandjws(Section 10.16.2). -
interactsays how the AS can reach a human. Start modes areredirect,app,user_codeanduser_code_uri; finish methods areredirect(browser callback) andpush(AS POSTs to the client). Thenonceis mandatory for all finish methods, which gives you CSRF and replay protection that OAuth leaves to the optionalstateparameter. -
subjectasks for identity information in the same call, which is why GNAP can stand in for a separate OIDC authentication request.
The response and the continuation loop
When user interaction is needed, the AS replies with a redirect URI, a finish value and a continuation handle (Section 3):
{ "interact": { "redirect": "https://server.example.com/interact/4CF492MLVMSW9MKMXKHQ", "finish": "MBDOFXG4Y5CVJCX821LH" }, "continue": { "access_token": { "value": "80UPRY5NM33OMUKMKSKU" }, "uri": "https://server.example.com/tx" } } The client sends the user to interact.redirect. After approval, the AS sends the user back to the client's finish uri with an interact_ref, and the client closes the transaction with a continuation request. Per Section 5.1 it is a signed POST using the GNAP scheme:
POST /continue HTTP/1.1 Host: server.example.com Content-Type: application/json Authorization: GNAP 80UPRY5NM33OMUKMKSKU Signature-Input: sig1=... Signature: sig1=... { "interact_ref": "4IFWWIKYBC2PQ6U56NL1" } Two things differ from OAuth's token endpoint. First, the continuation token is itself a bound credential: the client "MUST sign all continuation requests with its key" (Section 3.1). Second, the same continuation URI supports more verbs: POST without a body to poll, PATCH to modify the grant mid-flight, and DELETE to cancel it (Sections 5.2 to 5.4). If the AS gives a wait value (never less than five seconds), polling sooner earns a too_fast error.
That modify-in-flight capability is the "negotiation" in the name. A client can start with a small request, learn what the user will approve, and ask for more without restarting the whole flow.
Where GNAP is clearly better than OAuth 2.0
- Software-only and asynchronous approval. A backend agent can request access, the AS can contact a human out of band, and the client polls the continuation URI. Compare this with the device code flow, which has well-known phishing exposure because the user types a code they did not initiate.
- Different user at the client and at the AS. OAuth 2.0 assumes they are the same person. In GNAP, the approver can be someone else entirely, which fits delegated-approval workflows and agentic AI that needs a human sign-off.
- Unregistered clients. Open ecosystems where per-client registration does not scale (think Dynamic Client Registration pain) can use keys directly.
- One request shape. No more choosing among authorization code, device, CIBA and client credentials at design time.
Where it is not ready
- Ecosystem. Searching for libraries and IdP support turned up the RFC and commentary, but no confirmed GNAP support in Keycloak, Okta, Auth0 or Entra ID. Simon Willison's October 2024 write-up noted that two of the listed example implementations had not been committed to in over four years. Treat any claim of "production GNAP" skeptically until you have run the server's conformance behavior yourself.
- Resource server connections. The core spec defers AS-to-RS interoperability (token introspection, key binding at the RS) to a companion document, which RFC 9635 cites as a work-in-progress draft. Without it, you cannot build a multi-vendor deployment from the core RFC alone.
- Compatibility. RFC 9635 says GNAP is not meant to be directly compatible with OAuth 2.0. It includes migration examples, but existing API gateways, SDKs and token validation middleware do not understand
Authorization: GNAP. - Operational cost. Every request is key-signed. HTTP Message Signatures are the default
httpsigproof, and signing and verifying them correctly (canonicalization,tag="gnap", clock skew) is more work than validating a bearer JWT.
A pragmatic adoption path
Hold off on a protocol swap. Instead, take the GNAP ideas that OAuth already supports:
- Move high-value APIs to sender-constrained tokens with DPoP. It gives you GNAP-style key proof inside OAuth.
- Replace broad scopes with RAR
authorization_detailsfor fine-grained consent (see the OAuth 2.0 developer guide). - Keep refresh token lifetimes short or rotate them; the GNAP position is that rotation makes separate refresh tokens unnecessary.
- Prototype GNAP only where OAuth is a poor fit: asynchronous human approval, or software agents that need delegated authority. For agent scenarios, compare with OAuth token exchange and the MCP OAuth 2.1 approach.
- Inspect any tokens you are prototyping with the JWT Decoder.
Verdict
GNAP is a well-reasoned redesign, and Appendix A of RFC 9635 is worth reading even if you never deploy it. As of November 2026 it is a standard without a visible mainstream implementation base, so the production answer is "not yet" for most teams. Build on OAuth 2.1 plus DPoP and RAR today, watch for IdP announcements, and revisit when an AS you already run ships a GNAP endpoint.
原文:https://www.a8q9.cn/en/article/gnap-vs-oauth-2-0-is-rfc-9635-ready-for-production-4defaa.html