feat(oauth): name and show the icon of the requesting app on the consent screen (BUNYIP-406) #406
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/BUNYIP-406-consent-app-identity"
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?
What
The consent dialog read only "An application is requesting access to your information." It now names - and, when set, shows the icon of - the requesting app, and its copy names the user's Bunyip account.
Root cause of the missing name (confirmed)
The mokosh-apps client (the app users authorize from) was registered first-party, which by design skips the consent screen (BUNYIP-342). Production only showed the generic dialog because it is stale (predates BUNYIP-342's first-party skip + name threading). The code already names third-party clients correctly.
Per the confirmed decision, mokosh-apps now shows a named consent: the migration clears its
first_partyflag and sets its display name to "Mokosh" (was the internal "mokosh-apps"). Consent only appears for un-consented scopes, so users who already granted are not re-prompted.Icon
logo_uricolumn onoauth_clients, loaded on the existing runtimeOAuthClientquery (FromRow - no.sqlxregen) and threaded through the consent redirect the same wayclient_namealready is.<img>when a safe URL is set, otherwise a name-initial badge, so the card always shows a clean identity. Mokosh ships with no logo (there is no Mokosh logo asset yet), so it shows the "M" badge; a real logo drops in later via the column with no code change.Copy
"{name} is requesting access to your Bunyip account."Security
Name and logo URL are rendered by Maud as escaped text/attributes (no markup injection). Additionally only
https/ same-origin logo URLs are emitted as an<img>-javascript:/data:/ plaintexthttp:degrade to the initial badge and are never requested - matching the page CSP.Acceptance criteria
<img>.Verification
just check-containergreen: fmt + clippy-D warnings+ all test binaries (124 web tests). New tests: Bunyip-account copy, logo-scheme allowlist, and escaping of a spoofed name/logo.name -> Mokosh,first_party -> false,logo_uriTEXT/nullable.Note:
load_clientis a runtimequery_as(FromRow), so adding the column needed no.sqlxregeneration.🤖 Generated with Claude Code
The consent dialog read only "An application is requesting access to your information", never naming the requester. Two things fixed. Root cause of the missing name: the mokosh-apps client (the app users authorize from) was registered first-party, which by design SKIPS the consent screen (BUNYIP-342); production only showed the generic dialog because it is stale (predates that + the BUNYIP-342 name threading). Per the confirmed decision, mokosh-apps now shows a NAMED consent - the migration clears its first_party flag and sets its user-facing display name to "Mokosh" (was the internal "mokosh-apps"). Consent only appears for un-consented scopes, so users who already granted are not re-prompted. Application icon: a nullable logo_uri column on oauth_clients, loaded on the existing runtime OAuthClient query (FromRow, so no .sqlx regen) and threaded through the consent redirect the same way client_name already is. bunyip-web renders it as an app-identity row on the card. When no logo is set (the Mokosh default for now - there is no Mokosh logo asset yet) it degrades to a name-initial badge, so the card always shows a clean identity and the real logo drops in later with no code change. Copy now names the account: "{name} is requesting access to your Bunyip account." Security: the name and logo URL are rendered by Maud as escaped text/attributes, so a spoofed value cannot inject markup; additionally only https / same-origin logo URLs are emitted as an <img> (javascript:/data:/http degrade to the initial badge and are never requested), matching the page CSP. Verified: `just check-container` green (fmt + clippy -D warnings + all test binaries, 124 web tests). New tests cover the Bunyip-account copy, the logo-scheme allowlist, and that a spoofed name/logo is escaped and a hostile scheme yields no <img>. The migration was applied against a throwaway Postgres (name -> Mokosh, first_party -> false, logo_uri TEXT/nullable). The real Mokosh->Bunyip observed run (naming Mokosh in the dialog) is for post-deploy confirmation. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L5dcYueNHByRnWJDYoDX1WPopulate the Mokosh client's logo_uri with the real Mokosh brand mark instead of leaving it to the initial-badge fallback. The logo (mokosh-apps/assets/icon-192.png) is self-hosted by bunyip-web at assets/mokosh-logo.png and referenced same-origin ('/assets/mokosh-logo.png'), so the consent icon always loads, needs no cross-origin/CORS, and satisfies the page CSP ('self') - rather than depending on the Mokosh origin's asset paths or uptime. The migration's UPDATE now sets logo_uri alongside name/first_party for the mokosh-apps client. Verified against a throwaway Postgres (name=Mokosh, first_party=false, logo_uri=/assets/mokosh-logo.png). No Rust change; the rendering + escaping mechanism landed in the previous commit. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L5dcYueNHByRnWJDYoDX1W