Conversation
…ization response carries RFC 9207 `iss` Companion to nevermined-io/nvm-monorepo#3532 (nvm-monorepo#3527, epic #3458). - overview: the RFC 8414 example gains `authorization_response_iss_parameter_supported: true` and a section on validating `iss` (equal to the tier's `issuer`; success and denial; the device flow has no authorization response). - oauth-authorization-code: a prerequisite on which consent is being asked for — `agent_id` vs account-level, `consent_type=commerce` for a hand-configured provider, `scope=commerce` for a discovery-driven client, explicit wins, a contradiction is refused; `iss` on the hand-off; the flag in the discovery note. - for-agents: the two extra facts the document tells an agent (`iss`, and `scope=commerce` as the one scope the consent screen honours despite no `scopes_supported`). - integrations/mcp: a note on consent selection through `scope` and on `iss`. - api-reference/oauth-openapi.json (hand-maintained): the flag in the description + example, edited as text. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Member
|
No description provided. |
aaitor
reviewed
Sep 15, 2026
aaitor
left a comment
Member
There was a problem hiding this comment.
Reviewed the current head. One documentation consistency issue is inline.
| - **A pre-registered `client_id`.** Same as the device flow — connectors are onboarded out of band (no dynamic client registration). An unregistered `client_id` is rejected with `BCK.OAUTH.0016`. | ||
| - **PKCE is mandatory.** Generate a `code_verifier` and its `code_challenge` (`S256`). There is no non-PKCE path. | ||
| - **The right tier's authorize URL.** The browser step starts at the `authorization_endpoint` of the tier you are integrating with — `https://nevermined.app/oauth/authorize?network=sandbox` or `…?network=live` — as advertised by that tier's [RFC 8414 document](/integrate/authentication/overview#each-tier-is-its-own-issuer-and-its-authorize-url-says-so). This is the **web app's consent page**, the URL a connector sends the user's browser to with `client_id`, `redirect_uri`, `code_challenge` and the rest in its **query string** — keep the `network` parameter when you add them; it is what makes the consent screen open on the tier your `client_id` is registered on. It is not the API's `POST /oauth/authorize` in Step 1 below, which the signed-in browser client calls on the API host with those same fields in a **JSON body** (and no `network` — by then the tier is fixed). The consent URL is the only one of the two an integrator ever configures by hand; if you do, paste it with its query string. | ||
| - **Which consent you are asking for.** An `agent_id` on the authorize URL asks for an agent-specific grant (this page's flow). Without one, the consent is **account-level**, and its kind is named on the URL: `consent_type=commerce` for a spend cap the user funds from their Nevermined wallet for paying external services through the Router; nothing (or `consent_type=account_access`) for a card-backed account mandate. A **discovery-driven** client — one that took the endpoint from the RFC 8414 document and never saw a `consent_type` — selects the commerce consent with the standard OAuth `scope` parameter instead: `scope=commerce` (space-separated per RFC 6749 §3.3; extra scopes such as `offline_access` are tolerated and ignored) is treated as `consent_type=commerce`. An explicit `consent_type` always wins, and the two must not disagree — `consent_type=account_access&scope=commerce`, or `scope=commerce` together with an `agent_id`, is refused with an error rather than resolved silently. A hand-configured provider (LangSmith Fleet's Agent-Auth, say) keeps stating `?consent_type=commerce` on its authorize URL; `scope` is for clients that cannot. `scope` is not advertised in the discovery document (Nevermined publishes no `scopes_supported` — the API layer enforces no scope vocabulary) and never reaches the API: it only selects which consent screen the human sees. |
Member
There was a problem hiding this comment.
should-fix: This says an explicit consent_type “always wins,” but the next clause and selectConsentType in nvm-monorepo#3532 reject consent_type=account_access with scope=commerce as contradictory. Those are different precedence rules in the same sentence. Please say that the explicit value is preserved only when scope does not contradict it, or simply that contradictions are rejected.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Companion to nevermined-io/nvm-monorepo#3532 (implements nevermined-io/nvm-monorepo#3527, part of epic nevermined-io/nvm-monorepo#3458 — ADR 0077). Documents two AS-side behaviours that PR ships; merge after it deploys so the docs never lead the API.
What changed
integrate/authentication/overview.mdx— the RFC 8414 example gainsauthorization_response_iss_parameter_supported: true, and a new section The authorization response names its issuer (RFC 9207):isson the?code=success and the?error=denial alike, equal to the tier'sissuer, compare by plain string equality and reject a mismatch; the device flow has no authorization response.integrate/authentication/oauth-authorization-code.mdx— a prerequisite on which consent you are asking for:agent_id→ agent-specific; otherwise account-level,consent_type=commercefor a hand-configured provider (LangSmith Fleet keeps its?consent_type=commerce),scope=commercefor a discovery-driven client that never sawconsent_type; an explicitconsent_typewins;consent_type=account_access&scope=commerceorscope=commerce+agent_idis refused, never guessed;scopeis not advertised (noscopes_supported, unchanged) and never reaches the API.isson the hand-off, and the flag in the discovery note.integrate/authentication/for-agents.mdx— the two extra facts the document tells an agent (iss;scope=commerceas the one scope the consent screen honours).integrations/mcp.mdx— a note on consent selection throughscope(MCP clients forward thescopefrom the server'sWWW-Authenticatechallenge) and oniss, including why ChatGPT cares (stable CIMD client identity).api-reference/oauth-openapi.json(hand-maintained, per the docs CLAUDE.md exception) — the flag in the description and the example, edited as text.Deliberately not touched:
products/catalog/mcp.mdx's "OAuth-connector variant … tracked elsewhere" — that is the Commerce MCP's own challenge/PRM (nvm-monorepo#3459) and the verification matrix (#3460), documented when those land.api-reference/**SDK-sourced trees untouched.Verification
mintlify broken-links→success no broken links found(run from a worktree offorigin/main; no bare<placeholder>in the diff, so the check did run).🤖 Generated with Claude Code