fix: Bind consent to the server-side validated authorization request #72
No reviewers
Labels
No labels
kind
bug
kind
enhancement
wayfinder
grilling
wayfinder
map
wayfinder
prototype
wayfinder
research
wayfinder
task
workflow
needs-info
workflow
needs-triage
workflow
ready-for-agent
workflow
ready-for-human
workflow
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
rosa/vernier!72
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/consent-bound-to-validated-request"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Fixes #48.
A tampered
POST /auth/consentcould widen the OAuth grant and smuggle an unvalidated PKCE challenge: the consent form's hidden fields were trusted to rebuild the request, so the domain's scope clamp filteredselectedagainst arequestedset the form itself supplied, and the challenge entered throughCodeChallenge::from_storage, skipping S256 validation.Changes
GET /authparks the validatedAuthorizationRequestin the session under an opaque UUID. This replaces the raw-query parking across the login redirect too, so there is a single mechanism for "an authorization request in flight": after login the user is bounced to a bare/auth, which resumes the parked request.request_id, the scope checkboxes, and the approve/deny action. The hiddenclient_id,redirect_uri,state,code_challenge, andrequested_scopefields are gone from the template andConsentForm.POST /auth/consentconsumes the parked request before anything else (a decision is final and cannot be replayed), rejects a missing or mismatched id with a plain 400 — never redirecting to a client-supplied URI — and re-derives the entire grant from the stored request.AuthorizationConsentnow has private fields and is only constructible viaapprove(request, selected)/deny(request), socomplete_authorizationclamps against arequestedset the server validated.CodeChallenge::parse;from_storage's docs now pin it to repository rehydration, its sole remaining caller (it stayspubbecause the repository lives in theinfracrate).AuthorizationRequestand its component types gained serde derives so the web layer can park them in the session, with docs noting that this path — likefrom_storage— must never see user input.Acceptance criteria
indieauth_consent_cannot_widen_the_grant_beyond_the_validated_request), plus a domain-level clamp test.code_challenge; smuggled form fields are ignored and the code stays bound to the validated challenge (indieauth_consent_form_cannot_influence_the_code_challenge).indieauth_consent_with_an_unknown_request_id_is_rejected).indieauth_consent_cannot_be_replayed).indieauth_unauthenticated_authorization_returns_to_consent_after_login).cimise task passes.