The connection ceremony - #13
Merged
Merged
Conversation
A resource that fronts an upstream the person must link mints a
CONNECTION-ONLY resource token: no `scope`, no `r3_*`, and an
`interaction_code` naming the pending record it is holding. The PS issues
nothing for it. It puts the person in front of the resource's own published
`interaction_endpoint` with that code and a callback, and the record
terminates when the resource bounces the browser back —
`connection_established`, and no auth token.
mockin had none of it. A scope-less token was read as `scope: ''` and got an
auth token, so an agent driving a connect against mockin was told it had
connected while no upstream OAuth had happened at all. Hellō's Wallet shipped
the ceremony in #4269; the reference PS never learned it, which left the whole
feature testable only against beta.
- `verify-resource-token.js`: absent `scope` (distinct from `''` — an existing
R3 test mints an empty one) plus `interaction_code` is a connection; `r3`
without scope is rejected; the retired nested `interaction` object is
rejected.
- `token.js`: a connection creates a `kind: 'connection'` pending and answers
202 `requirement=interaction; code="…"` — code only, no `url=`, because the
recipient composes it from the resource's `interaction_endpoint` (which must
be published, and https).
- `consent.js`: a connection is not the PS's to approve — it redirects to
`{interaction_endpoint}?code={interaction_code}&callback={ISSUER}/aauth/bounce/{code}`.
- `GET /aauth/bounce/:code`: the resource returning the browser, which is what
terminates the record. `?error=` fails it instead.
- `pending.js`: an approved connection polls `200 { status:
'connection_established' }`.
Eight tests in `test/aauth/connection.spec.js`. 241 passing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CRdau7tgZa1tPVzUdyNrHc
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.
Found while building the MCP end-to-end suite in
aauth-proxy(hellocoop/aauth-proxytest/e2e-compose):connect_resourcereportedconnectedagainst mockin while no upstream OAuth exchange had happened at all.mockin has no notion of the connection ceremony. A connection-only resource token — no
scope, nor3_*, aninteraction_codenaming the pending record the resource is holding — was read asscope: ''and got an auth token. Hellō's Wallet shipped the ceremony in Wallet#4269; the reference PS never learned it, which left the entire feature testable only against beta Wallet.This is Q7 in
ONBOARDING-PLAN-2.md("mockin learns the connection-only ceremony — yes if cheap"), deferred in session 2. It was not cheap, but it is the thing standing between us and an e2e that means anything.What the ceremony is
The PS issues nothing. It holds a pending record, sends the person to the resource's own published
interaction_endpointwith the code the resource is holding, and the record terminates when the resource bounces the browser back —connection_established, no auth token. The resource finishing is the event, not the person arriving at the PS.Changes
verify-resource-token.jsscope(distinct from''— an existing R3 test mints an empty one) plusinteraction_codeis a connection;r3without scope rejected; the retired nestedinteractionobject rejectedtoken.jskind: 'connection'pending and answers202 requirement=interaction; code="…"— code only, nourl=, because the recipient composes it frominteraction_endpoint(which must be published, and https)consent.js{interaction_endpoint}?code={interaction_code}&callback={ISSUER}/aauth/bounce/{code}consent.jsGET /aauth/bounce/:code— the resource returning the browser, which terminates the record.?error=fails it insteadpending.js200 { status: 'connection_established' }The callback contract matches what the fleet already implements:
core/oauth.tsreads?callback=on/oauth/startand redirects there when the upstream OAuth completes.Eight tests in
test/aauth/connection.spec.jscover the 202 shape, the redirect target, termination on the bounce rather than on consent, the error bounce, the two rejections, empty-vs-absent scope, and the capability gate.241 passing.
🤖 Generated with Claude Code
https://claude.ai/code/session_01CRdau7tgZa1tPVzUdyNrHc