Skip to content

Show a success screen when the OAuth redirect targets a native app#3107

Merged
ChiragAgg5k merged 2 commits into
mainfrom
feat/oauth2-native-redirect-success
Jul 6, 2026
Merged

Show a success screen when the OAuth redirect targets a native app#3107
ChiragAgg5k merged 2 commits into
mainfrom
feat/oauth2-native-redirect-success

Conversation

@ChiragAgg5k

Copy link
Copy Markdown
Member

What does this PR do?

When an OAuth2 client's redirect URI uses a custom protocol (e.g. cursor:// for Cursor), the browser hands the redirect off to the OS and the consent tab is left hanging on the consent card with no feedback. This PR detects non-web (http/https) redirect URIs after approve/reject and renders an outcome screen instead.

  • approve()/reject() in the consent card now report completion via a unified onDone(outcome, redirectUrl) callback when the redirect is a native deep link (or when there is no redirect), instead of the device-only onDeviceDone.
  • New shared outcome-card.svelte renders the terminal state for both the authorization and device flows. It completes the consent card's identity row: the dashed "pending" connector between the app and account avatars resolves to a solid line with a check node when approved, and stays dashed with a neutral X when cancelled.
  • On success with a deep link, an Open {app} button retries the redirect for users who dismissed the OS "open application" prompt.
  • The already-consented fast path (authorize returns a redirectUrl immediately) shows the same screen for native redirects.
  • The device flow's approved/denied screens were replaced with the shared card, removing the duplicated markup.

Screenshots

Access granted (desktop / mobile)

oauth-consent-success-desktop

oauth-consent-success-mobile

Request cancelled (desktop / mobile)

oauth-consent-denied-desktop

oauth-consent-denied-mobile

Test plan

  • bun run check, bun run lint, bun run test:unit (235 passed), bun run build all pass.
  • Verified in the browser (Playwright against the dev server with mocked API responses): approved via immediate-redirect path, and denied via the consent card's Cancel button, on desktop and mobile viewports in dark mode.

Related PRs and Issues

  • None

When an OAuth2 client redirects to a custom protocol (e.g. cursor://), the browser hands off to the OS and the tab is left hanging with no feedback. Detect non-web redirect URIs after approve/reject and render an outcome screen instead. The shared outcome card completes the consent card's identity row (dashed pending connector resolves to a check when approved, X when cancelled) and offers an Open-app button to retry the deep link. The device flow's approved/denied screens now reuse the same card.
@greptile-apps

greptile-apps Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a terminal outcome screen for OAuth2 consent flows that target native apps (custom protocol redirect URIs such as cursor://). When the browser hands off a deep link to the OS the consent tab was previously left stranded on the consent card; it now transitions to a shared outcome-card that reflects the approved/denied state and offers an "Open {app}" retry button.

  • isWebRedirect is extracted to a new shared helper (oauth2-redirect.ts), onDeviceDone is replaced by a unified onDone(outcome, redirectUrl?) callback in consent-card.svelte, and the "already consented" fast path in consent/+page.svelte correctly fetches app branding before showing the outcome screen (with a cancelled guard in place after the async call).
  • The new outcome-card.svelte is built with Svelte 5 runes, conditionally renders the connector/account avatar only when accountLabel is present (falling back to a corner badge otherwise), and the device-flow duplicated markup is removed in favour of the shared card.

Confidence Score: 5/5

The change is well-scoped to the OAuth2 public routes and introduces no new API surface or auth logic; the native-redirect detection path is straightforward and existing web-redirect behaviour is unchanged.

All async boundaries carry proper cancellation guards, the shared isWebRedirect helper consolidates the previously duplicated logic, and the outcome card gracefully handles both null app and absent accountLabel. No correctness issues were found.

No files require special attention.

Important Files Changed

Filename Overview
src/lib/helpers/oauth2-redirect.ts New shared utility exposing isWebRedirect; clean extraction that eliminates the duplication raised in the previous review thread.
src/routes/(public)/oauth2/consent-card.svelte Replaces onDeviceDone with the unified onDone(outcome, redirectUrl?) callback; native-redirect detection is correct — window.location.href fires first then the guard runs synchronously, so the outcome screen is only shown when the page actually stays open.
src/routes/(public)/oauth2/consent/+page.svelte Adds approved/denied phases and the "already consented" native-redirect fast path; the if (cancelled) return guard is correctly placed after the async apps.get() call, addressing the concern from the prior review thread.
src/routes/(public)/oauth2/device/+page.svelte Replaces duplicated inline approved/denied markup with OAuth2OutcomeCard; onDone signature is compatible (TypeScript permits fewer params than the declared optional second argument).
src/routes/(public)/oauth2/outcome-card.svelte New component using Svelte 5 runes; correctly guards the connector/account avatar behind {#if accountLabel} and falls back to a corner status-badge, handling both flows and the null-app case gracefully.

Reviews (2): Last reviewed commit: "(fix): address review comments on OAuth ..." | Re-trigger Greptile

Comment thread src/routes/(public)/oauth2/consent/+page.svelte
Comment thread src/routes/(public)/oauth2/outcome-card.svelte Outdated
Comment thread src/routes/(public)/oauth2/consent/+page.svelte Outdated
Guard the already-consented fast path against a cancelled effect, hide the account avatar and move the status onto the app avatar when no account label is known, and extract the duplicated isWebRedirect helper to $lib/helpers/oauth2-redirect.
@ChiragAgg5k ChiragAgg5k merged commit ebe5e78 into main Jul 6, 2026
4 checks passed
@ChiragAgg5k ChiragAgg5k deleted the feat/oauth2-native-redirect-success branch July 6, 2026 10: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.

2 participants