Skip to content

fix(auth): preserve intended URL through login redirects - #1650

Open
brianphillips wants to merge 2 commits into
sourcebot-dev:mainfrom
brianphillips:login-preserve-next-url
Open

fix(auth): preserve intended URL through login redirects#1650
brianphillips wants to merge 2 commits into
sourcebot-dev:mainfrom
brianphillips:login-preserve-next-url

Conversation

@brianphillips

@brianphillips brianphillips commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Fixes #1649

Summary

  • Preserve the original path and query string when auth guards send users to login.
  • Keep search and Ask/chat URLs across OAuth, credentials, magic-link, and email-code flows.
  • Validate callback URLs as same-origin relative paths.

Verification

  • yarn workspace @sourcebot/web test src/lib/authRedirect.test.ts --run
  • yarn workspace @sourcebot/web exec tsc --noEmit
  • yarn workspace @sourcebot/web lint

No visual changes.


Note

Medium Risk
Touches authentication redirect boundaries across login guards and callback URL handling; validation reduces open-redirect risk but mis-handled callbacks could still send users to the wrong page after sign-in.

Overview
Fixes login redirects dropping query strings so users return to the same search or Ask/chat URL after signing in.

Introduces normalizeCallbackUrl and createLoginUrl in authRedirect.ts (with unit tests) to keep only safe same-origin relative paths and reject open-redirect targets. The edge proxy now stamps x-sourcebot-request-path with the current path and search; the app layout, auth middleware, GCP IAP, and account-linking flows use that value when sending users to login or post-auth callbacks.

Login, signup, magic-link verification, OAuth/credentials flows, and in-app sign-in links (chat share, banners, MCP connectors, login dialog) now pass the full pathname?query through callbackUrl instead of path-only or /.

Reviewed by Cursor Bugbot for commit b6b0b93. Bugbot is set up for automated code reviews on this repo. Configure here.


Summary by cubic

Fixes #1649. Auth guards now preserve the original path and query string when redirecting to login, so users land back on the page they intended after authenticating instead of the home page. Callback URLs are now validated as same-origin relative paths before use to prevent open redirects.

Bug Fixes

  • Adds normalizeCallbackUrl and createLoginUrl helpers with unit tests in packages/web/src/lib/authRedirect.ts.
  • Captures the intended URL server-side via the x-sourcebot-request-path header set in proxy.ts and read by the app layout and authenticated page middleware.
  • Applies the same normalizing logic to the login, signup, magic-link verification, and in-page sign-in prompt and dialog flows.
  • Already-authenticated users visiting /login or /signup are now redirected to the requested URL instead of /.

Written for commit b6b0b93. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Bug Fixes

    • Preserved the current page and search parameters when signing in, using magic links, verifying accounts, or creating an account.
    • Returned users to their original destination after authentication instead of always sending them to the home page.
    • Improved login redirects from chat, sharing, connector, and account-linking flows.
    • Prevented unsafe or external callback URLs from being used.
  • Tests

    • Added coverage for preserving valid callback URLs and rejecting unsafe redirect destinations.
  • Documentation

    • Documented the updated login redirect behavior in the unreleased changelog.

@coderabbitai

coderabbitai Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 05d971e7-8b05-4127-a0a0-a77eae74bd88

📥 Commits

Reviewing files that changed from the base of the PR and between b95ad90 and b6b0b93.

📒 Files selected for processing (17)
  • CHANGELOG.md
  • packages/web/src/app/(app)/chat/components/shareChatPopover/shareSettings.tsx
  • packages/web/src/app/(app)/layout.tsx
  • packages/web/src/app/components/authMethodSelector.tsx
  • packages/web/src/app/login/components/loginForm.tsx
  • packages/web/src/app/login/components/magicLinkForm.tsx
  • packages/web/src/app/login/page.tsx
  • packages/web/src/app/login/verify/page.tsx
  • packages/web/src/app/login/verify/verifyForm.tsx
  • packages/web/src/app/signup/page.tsx
  • packages/web/src/ee/features/chat/components/chatThread/signInPromptBanner.tsx
  • packages/web/src/ee/features/chat/mcp/components/connectorsMenu.tsx
  • packages/web/src/features/chat/components/chatBox/loginDialog.tsx
  • packages/web/src/lib/authRedirect.test.ts
  • packages/web/src/lib/authRedirect.ts
  • packages/web/src/middleware/authenticatedPage.tsx
  • packages/web/src/proxy.ts

Included review availability: Your plan provides up to 8 included reviews per hour; 7 remain after this review.


Walkthrough

Authentication redirects now preserve validated paths and query strings across login, signup, verification, OAuth, magic-link, credential, chat, and request-header flows.

Changes

Authentication callback URL preservation

Layer / File(s) Summary
Callback URL validation and construction
packages/web/src/lib/authRedirect.ts, packages/web/src/lib/authRedirect.test.ts
Adds shared callback URL validation, login URL construction, request-header naming, and tests for accepted and rejected URL forms.
Request path propagation into authentication redirects
packages/web/src/proxy.ts, packages/web/src/app/(app)/layout.tsx, packages/web/src/middleware/authenticatedPage.tsx
Propagates the request pathname and query string through REQUEST_PATH_HEADER and uses it for authentication redirects and callback props.
Normalized callback handling across login flows
packages/web/src/app/components/authMethodSelector.tsx, packages/web/src/app/login/..., packages/web/src/app/signup/page.tsx
Normalizes callback URLs across OAuth, credentials, magic-link, login, signup, and verification flows.
Query-preserving login entry links
packages/web/src/app/(app)/chat/..., packages/web/src/ee/features/chat/..., packages/web/src/features/chat/..., CHANGELOG.md
Login links now include the current pathname and query string. The changelog records the fix.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Bug fix · Severity of issue fixed: Medium

Suggested reviewers: brendan-kellam

Merge Risk: ⚪ Minimal · up to b6b0b

Authentication redirects preserve safe original destinations across the updated login flows, with no unresolved material risk identified.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 9 functions across 16 files. (1 skipped: … Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: preserving the intended URL through authentication redirects.
Linked Issues check ✅ Passed Issue #1649 requires the exact original path and query string after credentials, SSO, and magic-link authentication. proxy.ts captures pathname and search in x-sourcebot-request-path. The login, s…
Out of Scope Changes check ✅ Passed The changes stay within Issue #1649. The shared redirect helpers, flow updates, middleware propagation, focused tests, and changelog entry support preservation of login destinations. No unrelated prod…
Full details: Docstring Coverage

Explanation

Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 9 functions across 16 files. (1 skipped: 1 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

2 issues found and verified against the latest diff

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/web/src/lib/authRedirect.ts">

<violation number="1" location="packages/web/src/lib/authRedirect.ts:29">
P0: normalizeCallbackUrl returns a protocol-relative `//evil.com` for crafted inputs like `/..//evil.com` or `/%2e%2e//evil.com`, because the URL parser removes the dot-segment and normalizes the following `//` into the pathname. The raw-input `startsWith('//')` guard (lines 17-18) and the origin check (line 25) both pass, so this value flows into `redirect(callbackUrl)` in login/page.tsx and verify/page.tsx, producing an open redirect to an external host. Check the parsed `url.pathname` for a leading `//` after the origin check and reject it.</violation>
</file>

<file name="packages/web/src/proxy.ts">

<violation number="1" location="packages/web/src/proxy.ts:41">
P3: The middleware sets x-sourcebot-request-path on every request, but only the (app) layout and authenticatedPage HOC read it. API routes, /login, /signup, /oauth, and static assets get an unused header plus a request rewrite. Restrict it to the (app) segment, or only compute the header on the routes that consume it.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment on lines +29 to +33
return `${url.pathname}${url.search}`;
} catch {
return '/';
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P0: normalizeCallbackUrl returns a protocol-relative //evil.com for crafted inputs like /..//evil.com or /%2e%2e//evil.com, because the URL parser removes the dot-segment and normalizes the following // into the pathname. The raw-input startsWith('//') guard (lines 17-18) and the origin check (line 25) both pass, so this value flows into redirect(callbackUrl) in login/page.tsx and verify/page.tsx, producing an open redirect to an external host. Check the parsed url.pathname for a leading // after the origin check and reject it.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/web/src/lib/authRedirect.ts, line 29:

<comment>normalizeCallbackUrl returns a protocol-relative `//evil.com` for crafted inputs like `/..//evil.com` or `/%2e%2e//evil.com`, because the URL parser removes the dot-segment and normalizes the following `//` into the pathname. The raw-input `startsWith('//')` guard (lines 17-18) and the origin check (line 25) both pass, so this value flows into `redirect(callbackUrl)` in login/page.tsx and verify/page.tsx, producing an open redirect to an external host. Check the parsed `url.pathname` for a leading `//` after the origin check and reject it.</comment>

<file context>
@@ -0,0 +1,37 @@
+            return '/';
+        }
+
+        return `${url.pathname}${url.search}`;
+    } catch {
+        return '/';
</file context>
Suggested change
return `${url.pathname}${url.search}`;
} catch {
return '/';
}
}
if (url.origin !== CALLBACK_URL_ORIGIN || url.pathname.startsWith('//')) {
return '/';
}
return `${url.pathname}${url.search}`;

Comment thread packages/web/src/proxy.ts

return NextResponse.next();
const requestHeaders = new Headers(request.headers);
requestHeaders.set(REQUEST_PATH_HEADER, `${request.nextUrl.pathname}${request.nextUrl.search}`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: The middleware sets x-sourcebot-request-path on every request, but only the (app) layout and authenticatedPage HOC read it. API routes, /login, /signup, /oauth, and static assets get an unused header plus a request rewrite. Restrict it to the (app) segment, or only compute the header on the routes that consume it.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/web/src/proxy.ts, line 41:

<comment>The middleware sets x-sourcebot-request-path on every request, but only the (app) layout and authenticatedPage HOC read it. API routes, /login, /signup, /oauth, and static assets get an unused header plus a request rewrite. Restrict it to the (app) segment, or only compute the header on the routes that consume it.</comment>

<file context>
@@ -36,5 +37,12 @@ export async function proxy(request: NextRequest) {
 
-    return NextResponse.next();
+    const requestHeaders = new Headers(request.headers);
+    requestHeaders.set(REQUEST_PATH_HEADER, `${request.nextUrl.pathname}${request.nextUrl.search}`);
+
+    return NextResponse.next({
</file context>

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.

[bug] Preserve search and Ask URLs through login redirects

1 participant