Skip to content

feat(oauth2): redesign cloud consent screen with RAR resource binding#3101

Merged
ChiragAgg5k merged 2 commits into
mainfrom
feat-oauth2-consent-redesign
Jul 1, 2026
Merged

feat(oauth2): redesign cloud consent screen with RAR resource binding#3101
ChiragAgg5k merged 2 commits into
mainfrom
feat-oauth2-consent-redesign

Conversation

@ChiragAgg5k

Copy link
Copy Markdown
Member

What & why

Reworks the OAuth2 consent screen for the new cloud RAR (RFC 9396) flow, where a client requests every privilege via scope and binds those privileges to concrete projects/organizations via authorization_details.

The screen now:

  • Shows the requested permissions as a clear, read-only summary.
  • Lets the user narrow the one thing they control — which projects/organizations each tier is bound to.
  • Sends back the user-chosen resource binding; scopes are never silently downscoped.

The old standalone scope picker is removed in favour of a read-only permission summary + a dedicated resource selector.

Changes

Consent model (oauth2-scopes.ts)

  • Group a resource's read/write scopes into a single row (e.g. tables.read + tables.write → one Tables row).
  • Supabase-style rows: resource-name titles, an access chip (READ / WRITE / READ + WRITE, amber when write is involved), and a full-line description.
  • Rows ordered by access strength — Read + Write, then Write, then Read.
  • Console-authored copy for every scope Cloud exposes (99 project + 10 org resources), replacing the terse server strings. This drops the live scope-catalog fetch and its network calls; unknown/future scopes still get a sensible fallback title.
  • Raw scope strings are no longer rendered inline — each row has a hover copy button that yields the exact token (prefix included).

Consent card (consent-card.svelte)

  • App-to-account handshake header, grouped surface panels, tidy footer.
  • Distinct Project access / Organization access panels with a searchable resource selector.
  • Amber "needs selection" state that blocks Authorize until each requested tier is bound to at least one resource.

Resource selector (resource-selector.svelte, new)

  • Segmented All / Specific control, searchable chips, clearer Change/Done toggle.

RAR binding (oauth2-authorization-details.ts)

  • Parse/merge/serialize authorization_details, resolve project/organization names, and search resources for the selector.

Behaviour preserved

Only appearance/copy changed on the interactive side. Approve/reject, the grant-swap guards, the "bind every requested tier or stay disabled" rule, and the approve payload (resource binding narrowed, scopes untouched) are unchanged.

Screenshots

Dark mode. Captured end-to-end against a local cloud instance.

Basic consent

flow-01-basic-consent

Many scopes — access chips, ordered by strength

flow-02-many-scopes

Project + Organization tiers

flow-03-project-and-org

More flows

Choosing specific projects

flow-04-specific-projects

Needs selection (blocks Authorize)

flow-05-needs-selection

Full account access (all scope)

flow-06-full-account-access

Invalid / expired request

flow-07-error

Copy raw scope (hover)

flow-08-copy-scope

Testing

  • bun run format · bun run check (0 errors) · bun run lint (0 errors) · bun run build
  • Manually verified each flow above against a local cloud instance.

Rework the OAuth2 consent screen for the cloud RAR (RFC 9396) flow.

Scopes now carry every requested privilege; authorization_details binds
the project/organization tiers to concrete resources, and the resource
binding is the only thing the user narrows on the screen.

UI/UX:
- Grouped surface panels, app-to-account handshake header, and a
  clearer permission list (replaces the old scope picker).
- Supabase-style permission rows: resource-name titles, an access chip
  (READ / WRITE / READ + WRITE, amber when write is involved), and a
  one-line description per resource. Rows ordered by access strength.
- Console-authored copy for every scope Cloud exposes (drops the terse
  live scope catalog + its network calls); sensible fallback for unknown
  scopes. Raw scope tokens are no longer shown inline — a hover copy
  button yields the exact token.
- Resource selector: segmented All/Specific control, searchable chips,
  clearer Change/Done toggle, and an amber "needs selection" state that
  blocks Authorize until each requested tier is bound.

Delete the standalone oauth2-scope-picker in favour of the read-only
permission summary + resource selector.
@greptile-apps

greptile-apps Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Reworks the OAuth2 consent screen for the RAR v2 contract, replacing the old scope picker with a read-only permissions summary and a new resource-selector component that lets users narrow project/organization bindings. The approve payload still never upscopes — only the resource binding is narrowed — and every previously flagged guard (grant-swap protection, blocked-until-bound logic) is preserved.

  • oauth2-scopes.ts: Drops the live scope-catalog network call in favour of a static console-authored copy map (109 resources); adds buildConsentPermissions to produce grouped, access-chipped permission lines.
  • oauth2-authorization-details.ts: Refactors helpers for the v2 project/organization detail types with wildcard support; adds server-search and organisation-list helpers for the new picker.
  • resource-selector.svelte (new): All/Specific segmented control, debounced server search, chip-based multi-select — all in Svelte 5 runes.

Confidence Score: 5/5

Safe to merge — no defects found in the consent flow, resource binding, or approve/reject paths.

The grant-swap guard, blocked-until-bound logic, and downscope-only approve payload are all intact and correct. The only findings are a missing .catch() that could leave a spinner stuck in an edge case that cannot currently occur, and dead teamId field selections in project queries.

resource-selector.svelte — the search effect find() promise has no .catch() handler.

Important Files Changed

Filename Overview
src/lib/helpers/oauth2-authorization-details.ts Rewrites RAR helpers for v2 contract: new project/organization types, wildcard support, serializeGrantedDetails simplified to resource-binding only, adds searchProjects/listOrganizationResources for the picker. teamId is unnecessarily included in Query.select but never consumed.
src/lib/helpers/oauth2-scopes.ts Replaces live scope-catalog fetch with a static console-authored copy map (109 resources), adds buildConsentPermissions to produce grouped read-only permission lines with access chips. Logic is correct; resource grouping and access-rank sorting look solid.
src/routes/(public)/oauth2/consent-card.svelte Full redesign of the consent UI using Svelte 5 runes: read-only permissions summary, project/organization resource selectors, grant-swap guards, and blocked state enforcing at least one resource per requested tier.
src/routes/(public)/oauth2/resource-selector.svelte New component: All/Specific segmented control, searchable chips, debounced server search. searching state has no .catch() guard on the find() promise, which could leave the spinner permanently shown if find ever rejects.
src/routes/(public)/oauth2/oauth2-scope-picker.svelte File deleted — old standalone scope picker replaced by the new resource-selector and read-only permissions summary in consent-card.

Reviews (2): Last reviewed commit: "(fix): key oauth2 consent rows by token ..." | Re-trigger Greptile

Comment thread src/routes/(public)/oauth2/consent-card.svelte
Comment thread src/routes/(public)/oauth2/consent-card.svelte Outdated
Addresses Greptile review: two resources whose names titleize to the
same string would collide when keyed by title. The token is the
space-joined raw scopes and is unique per row within a group.
@ChiragAgg5k ChiragAgg5k merged commit a7bb38e into main Jul 1, 2026
4 checks passed
@ChiragAgg5k ChiragAgg5k deleted the feat-oauth2-consent-redesign branch July 1, 2026 11:26
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.

3 participants