Skip to content

feat(oauth): remember consent, skip it for first-party apps, enforce it on prompt=none - #204

Merged
Mikefluff merged 2 commits into
mainfrom
feat/remembered-consent
Sep 26, 2026
Merged

Mikefluff merged 2 commits into
mainfrom
feat/remembered-consent

Conversation

@Mikefluff

@Mikefluff Mikefluff commented Sep 26, 2026 •

Copy link
Copy Markdown
Member

Consent is remembered per (user, client, scopes); first-party clients skip the screen — a switch per client in the admin (new admin-registered clients default to on; dcr_* can never be first-party; migration 0026 backfills every existing non-dcr client as first-party). prompt=none now answers consent_required instead of minting a code without consent. The account page gains Connected apps with disconnect. Migration 0026 is additive plus that backfill.

🤖 Generated with Claude Code

https://claude.ai/code/session_015G5qmCWXaguo6yxvfBMPTt

Mikefluff and others added 2 commits September 26, 2026 12:12
…it on prompt=none

Every interactive sign-in ended on the consent screen, for every
application, every time: nothing recorded what the person had approved.
A customer going from one INITE product to pay in another had to confirm
again. And prompt=none issued a code to any client for anyone with a
session, without checking consent at all (OIDC core 3.1.2.6 says answer
consent_required).

- oauth_consents records the scopes a person approved per client; the
  screen is shown again only for scopes not yet approved. Requests with
  RFC 9396 authorization_details are always asked — they are specific to
  the request.
- oauth_clients.firstParty: INITE's own applications need no consent.
  Set at boot from OAUTH_FIRST_PARTY_CLIENTS (the whole list when set;
  unset leaves the column alone); dcr_* clients are never first-party.
  The prod compose lists the manually registered INITE clients.
- /authorize with a session and nothing to ask returns the code directly;
  prompt=none answers consent_required when there is. The consent screen
  asks GET /oauth/consent-check on load and approves by itself when not
  needed, since password/passkey/wallet sign-in land on it directly.
- create-code records the approval. The account page gains "Connected
  apps": GET /oauth/consents, DELETE /oauth/consents/:clientId forgets the
  consent and revokes that client's refresh tokens.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015G5qmCWXaguo6yxvfBMPTt
The first-party list lived in OAUTH_FIRST_PARTY_CLIENTS and was applied at
boot, so marking an app meant a config change and a redeploy. It is now a
switch in the admin (create and edit panels, shown as a badge in the list
and details), sent as `firstParty` to POST/PUT /admin/oauth-clients.

- New admin-registered clients default to first-party; untick for a partner.
- A dcr_* (self-registered) client cannot be made first-party (400).
- Migration 0026 backfills: every existing client not named dcr_* is
  first-party. The env variable, the boot step and the compose entry go.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015G5qmCWXaguo6yxvfBMPTt
@Mikefluff
Mikefluff merged commit cf43b43 into main Sep 26, 2026
11 checks passed
@Mikefluff
Mikefluff deleted the feat/remembered-consent branch September 26, 2026 15:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant