Skip to content

[codex] fix OAuth redirect contract#1393

Merged
mantrakp04 merged 2 commits into
devfrom
codex/oauth-redirect-contract
Apr 28, 2026
Merged

[codex] fix OAuth redirect contract#1393
mantrakp04 merged 2 commits into
devfrom
codex/oauth-redirect-contract

Conversation

@mantrakp04
Copy link
Copy Markdown
Collaborator

@mantrakp04 mantrakp04 commented Apr 28, 2026

Summary

  • Route browser OAuth redirects through the configured redirectMethod instead of hardcoded window.location calls.
  • Keep OAuth redirect APIs pending after navigation starts, including custom redirect methods.
  • Add cliAuthConfirm handler URL metadata and custom-page prompt coverage.
  • Update SDK spec text for browser OAuth callback and returnTo behavior.

Root Cause

OAuth helpers previously combined URL construction with direct browser navigation. That bypassed configured redirect methods and made it too easy for public redirect APIs to resolve after navigation started.

Impact

Browser SDK consumers get consistent redirect behavior across built-in and custom navigation methods. returnTo is handled as the post-callback destination while the OAuth callback URL remains fixed to the configured handler route.

Validation

  • pnpm test run packages/template/src/lib/auth.test.ts
  • pnpm test run apps/e2e/tests/js/oauth.test.ts
  • pnpm -C packages/template lint
  • pnpm -C apps/e2e lint
  • pnpm -C packages/template typecheck
  • pnpm -C apps/e2e typecheck

Summary by CodeRabbit

  • New Features

    • Added CLI authorization confirmation page/flow for terminal-based auth.
    • Added optional returnTo parameter for OAuth to control post-auth redirects.
    • Exposed configurable redirect behavior so apps follow the chosen redirect method.
  • Bug Fixes

    • OAuth callback now uses app navigation/queued redirects and shows a fallback link instead of forcing location.assign.
  • Tests

    • Added unit and e2e tests covering OAuth URL generation, scope handling, and CLI auth confirmation.

@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 28, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
stack-auth-hosted-components Ready Ready Preview, Comment Apr 28, 2026 5:27pm
stack-backend Ready Ready Preview, Comment Apr 28, 2026 5:27pm
stack-dashboard Ready Ready Preview, Comment Apr 28, 2026 5:27pm
stack-demo Ready Ready Preview, Comment Apr 28, 2026 5:27pm
stack-docs Ready Ready Preview, Comment Apr 28, 2026 5:27pm
stack-preview-backend Ready Ready Preview, Comment Apr 28, 2026 5:27pm
stack-preview-dashboard Ready Ready Preview, Comment Apr 28, 2026 5:27pm

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 28, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5e6cb222-541e-478c-8b16-b58e54a9b1d2

📥 Commits

Reviewing files that changed from the base of the PR and between 8db0983 and c97f8b0.

📒 Files selected for processing (5)
  • apps/e2e/tests/js/oauth.test.ts
  • packages/template/src/components-page/oauth-callback.tsx
  • packages/template/src/components-page/stack-handler-client.tsx
  • packages/template/src/lib/stack-app/apps/implementations/client-app-impl.ts
  • packages/template/src/lib/stack-app/apps/interfaces/client-app.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/template/src/components-page/oauth-callback.tsx
  • packages/template/src/components-page/stack-handler-client.tsx

📝 Walkthrough

Walkthrough

Adds a CLI auth confirmation handler and updates OAuth redirect flows: helpers now return URLs, client redirects use a configurable redirectMethod via new internals, signInWithOAuth supports an optional returnTo routed through the OAuth callback, and related tests/docs updated.

Changes

Cohort / File(s) Summary
CLI Auth Confirmation
packages/stack-shared/src/interface/handler-urls.ts, packages/stack-shared/src/interface/page-component-versions.ts, packages/template/src/lib/stack-app/url-targets.ts
Adds cliAuthConfirm handler key, defines the SDK-managed cliAuthConfirm custom page prompt, and maps the handler to the cli-auth-confirm hosted path / resolution.
Redirect internals & client-app API
packages/template/src/lib/stack-app/apps/interfaces/client-app.ts, packages/template/src/lib/stack-app/apps/implementations/client-app-impl.ts
Exposes getRedirectMethod() and redirectToUrl(...) on stackApp internals; client implementation now uses getNewOAuthProviderOrScopeUrl() plus _redirectTo(...), enqueues/queues redirects, and adds redirectToCliAuthConfirm(...).
OAuth helper & URL generation
packages/template/src/lib/auth.ts, packages/template/src/lib/auth.test.ts, sdks/spec/src/apps/client-app.spec.md
Converts redirecting helper into getNewOAuthProviderOrScopeUrl(): Promise<string> (returns URL instead of navigating); adds tests validating generated OAuth URL and documents signInWithOAuth(..., options?) returnTo behavior.
Callback & handler client redirect behavior
packages/template/src/components-page/oauth-callback.tsx, packages/template/src/components-page/stack-handler-client.tsx
Routes error and success redirect logic through redirectToError/queued redirectTargets and uses app internals redirect method instead of direct window.location calls; effect dependencies adjusted to re-run on app.
E2E test for redirectMethod behaviors
apps/e2e/tests/js/oauth.test.ts
Updates existing test to set redirectMethod: "window" and adds a new test validating custom navigator handler behavior by stubbing navigation, patching globals, invoking signInWithOAuth("github"), and asserting navigation capture and pending sign-in state.

Sequence Diagram

sequenceDiagram
    actor User
    participant Client as Browser Client
    participant AppInternals as Redirect Manager
    participant AuthProvider as OAuth Provider
    participant Callback as OAuth Callback Handler

    User->>Client: signInWithOAuth(provider, { returnTo? })
    Client->>Client: getNewOAuthProviderOrScopeUrl(provider, afterCallbackRedirectUrl)
    Client-->>AppInternals: _redirectTo(oauthAuthorizeUrl)
    AppInternals->>AppInternals: getRedirectMethod()
    alt redirectMethod == "window"
        AppInternals->>Client: window.location.assign(oauthAuthorizeUrl)
    else redirectMethod == "navigate"
        AppInternals->>Client: useNavigate(oauthAuthorizeUrl)
    else redirectMethod == "none"
        AppInternals-->>Client: queue redirectTarget (render fallback)
    end
    Client->>AuthProvider: User authorizes (browser flow)
    AuthProvider->>Callback: Redirect back to oauthCallback
    Callback->>Callback: Exchange code -> token
    Callback->>AppInternals: _redirectTo(afterCallbackRedirectUrl)
    AppInternals->>Client: Navigate to return destination (per redirectMethod)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • nams1570
  • BilalG1

Poem

🐰 In a burrow of code I hopped with glee,
Redirects now follow the path set free,
A CLI nod, a browser's gentle nudge,
URLs return, no abrupt window judge,
Hooray — tiny hops, big auth harmony! 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title '[codex] fix OAuth redirect contract' accurately summarizes the main change: refactoring OAuth redirects to use configured redirectMethod instead of hardcoded window.location calls.
Description check ✅ Passed The description provides clear Summary, Root Cause, Impact, and Validation sections that comprehensively explain the changes, their reasoning, and how to verify them. All required context is present and well-structured.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/oauth-redirect-contract

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 28, 2026

Greptile Summary

This PR fixes the OAuth redirect contract by decoupling URL construction from navigation: getNewOAuthProviderOrScopeUrl (renamed from addNewOAuthProviderOrScope) now returns the URL instead of calling window.location.assign, and all call sites route through _redirectTo which respects the configured redirectMethod. The returnTo option is also fixed — it was previously incorrectly used as redirect_uri; it now correctly becomes afterCallbackRedirectUrl while oauthCallback remains the fixed redirect_uri. oauth-callback.tsx and stack-handler-client.tsx receive matching updates for error-path and handler redirects respectively, and a new cliAuthConfirm handler URL is wired throughout.

Confidence Score: 5/5

Safe to merge; only P2 findings, no runtime breakage identified.

No P0 or P1 issues found. The two P2 findings (unvalidated returnTo URL and silent second-redirect drop) are edge cases that don't affect the primary changed paths in normal use. The core fix is structurally sound and well-tested.

packages/template/src/lib/stack-app/apps/implementations/client-app-impl.ts (returnTo validation) and packages/template/src/components-page/stack-handler-client.tsx (redirectTargets priority)

Security Review

  • Unvalidated returnTo URL (client-app-impl.ts ~line 2802): The new options.returnTo parameter is resolved via constructRedirectUrl (which builds an absolute URL but performs no origin check) and forwarded to the server as afterCallbackRedirectUrl. An absolute cross-origin value such as "https://attacker.com" passes through unchanged, creating an open-redirect risk if the server-side handler does not independently validate the URL.

Important Files Changed

Filename Overview
packages/template/src/lib/auth.ts Renamed addNewOAuthProviderOrScope → getNewOAuthProviderOrScopeUrl; navigation removed and delegated to callers.
packages/template/src/lib/stack-app/apps/implementations/client-app-impl.ts All OAuth redirect sites now use _redirectTo(); returnTo correctly becomes afterCallbackRedirectUrl; P2: returnTo lacks trusted-domain validation.
packages/template/src/components-page/stack-handler-client.tsx React platform queues redirects into a local array; navigate called via useRef+useEffect; 'none' method gets a fallback MessageCard.
packages/template/src/components-page/oauth-callback.tsx Error redirects now go through redirectToUrl; 'none' redirect method shows a fallback link via redirectUrl state.
apps/e2e/tests/js/oauth.test.ts New e2e test verifies signInWithOAuth never settles after custom navigate fires; 5000ms sentinel gives comfortable margin.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[signInWithOAuth called] --> B[getNewOAuthProviderOrScopeUrl]
    B --> C[getOAuthUrl via Stack API]
    C --> D[Returns authorization URL]
    D --> E{redirectMethod?}
    E -->|window| F[window.location.assign]
    E -->|custom object| G[navigate fn called]
    E -->|nextjs| H[router.push]
    E -->|none| I[returns immediately]
    F --> J[neverResolve - stays pending]
    G --> J
    H --> J
    I --> J
Loading

Fix All in Claude Code Fix All in Cursor Fix All in Codex

Prompt To Fix All With AI
This is a comment left during a code review.
Path: packages/template/src/lib/stack-app/apps/implementations/client-app-impl.ts
Line: 2801-2807

Comment:
**`returnTo` not validated against trusted domains**

`constructRedirectUrl` resolves the URL but does not check whether it is same-origin or otherwise trusted. An absolute `returnTo` such as `"https://evil.com"` passes through unchanged and is forwarded to the server as `afterCallbackRedirectUrl`, which is later used for the post-OAuth redirect. Adding a trusted-origin check (similar to `_isTrusted`) here would provide defence-in-depth and improve debugging of misconfigured values, consistent with the team's validation policy for redirect URLs.

**Rule Used:** Always validate redirect URLs for security reasons... ([source](https://app.greptile.com/review/custom-context?memory=45e85dee-0c65-4d46-911a-297e806c23cd))

**Learned From**
[stack-auth/stack-auth#923](https://github.com/stack-auth/stack-auth/pull/923)

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: packages/template/src/components-page/stack-handler-client.tsx
Line: 240

Comment:
**`redirectTargets` push during render may be ignored when multiple redirects occur**

`redirectTargets` is a plain array initialised to `[]` on every render. Both `redirectIfNotHandler` (called inside `renderComponent`) and the `result.redirect` branch can push an entry, but only index `0` is ever consumed. In the old code `result.redirect` overwrote `window.location.href` last, so it "won"; with the new code the `redirectIfNotHandler` entry is always index `0` and the `result.redirect` entry is silently dropped. If both code paths can genuinely fire on the same render, the second redirect is lost without any error. Consider asserting that `redirectTargets.length <= 1` after rendering to surface this early.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (2): Last reviewed commit: "Enhance OAuth callback handling and redi..." | Re-trigger Greptile

Comment thread packages/template/src/components-page/stack-handler-client.tsx Outdated
Comment thread apps/e2e/tests/js/oauth.test.ts Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates the browser OAuth flow in the JS template SDK to consistently use the configured redirectMethod (instead of direct window.location navigation), and clarifies the redirect contract so the OAuth callback URL remains stable while returnTo controls post-callback destination.

Changes:

  • Route browser OAuth navigation through redirectMethod, and keep redirecting APIs pending once navigation begins.
  • Add cliAuthConfirm to handler URL metadata and custom-page prompt guidance.
  • Update SDK spec wording and add/extend tests to cover the new redirect contract and non-resolving behavior.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
sdks/spec/src/apps/client-app.spec.md Documents returnTo vs callback URL behavior and redirectMethod-based navigation in browser OAuth flows.
packages/template/src/lib/stack-app/url-targets.ts Adds cliAuthConfirm hosted-path mapping + URL resolution support.
packages/template/src/lib/stack-app/apps/implementations/client-app-impl.ts Uses URL-returning OAuth helper + _redirectTo instead of hardcoded redirects; adds redirectToCliAuthConfirm; fixes returnTo contract in OAuth authorize call.
packages/template/src/lib/auth.ts Replaces “redirecting helper” with getNewOAuthProviderOrScopeUrl that returns the URL without navigating.
packages/template/src/lib/auth.test.ts Adds unit coverage ensuring OAuth URL construction does not perform navigation.
packages/template/src/components-page/stack-handler-client.tsx Routes handler-page redirects through useNavigate (React platform) instead of direct window.location writes.
packages/template/src/components-page/oauth-callback.tsx Routes error redirects through configured navigation instead of window.location.replace.
packages/stack-shared/src/interface/page-component-versions.ts Adds cliAuthConfirm custom-page prompt metadata and example.
packages/stack-shared/src/interface/handler-urls.ts Extends handler URL type to include cliAuthConfirm.
apps/e2e/tests/js/oauth.test.ts Adjusts/extends e2e coverage for redirectMethod behavior and “promise stays pending after navigation starts”.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/template/src/components-page/oauth-callback.tsx Outdated
Comment thread packages/template/src/components-page/stack-handler-client.tsx
- Updated the OAuthCallback component to manage redirects more effectively by introducing a new `redirectToError` function, which handles error redirects based on the current redirect method.
- Adjusted the timeout duration in the OAuth test to ensure proper handling of asynchronous operations.
- Improved the StackHandlerClient to utilize a reference for navigation, allowing for more reliable redirect handling based on the current redirect method.

These changes aim to streamline the OAuth flow and improve error handling during redirects.
@mantrakp04 mantrakp04 merged commit e2dc5f5 into dev Apr 28, 2026
36 of 52 checks passed
@mantrakp04 mantrakp04 deleted the codex/oauth-redirect-contract branch April 28, 2026 23:34
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