Skip to content

[#330] Form URL not signed in redirect#331

Merged
DVidal1205 merged 2 commits intomainfrom
formredirects
Feb 7, 2026
Merged

[#330] Form URL not signed in redirect#331
DVidal1205 merged 2 commits intomainfrom
formredirects

Conversation

@Adr1an04
Copy link
Contributor

@Adr1an04 Adr1an04 commented Feb 7, 2026

Closes #330

Why

rn when users hit forms, they are prompted to sign in which most of the time they are not. This then does not route them to forms but to the dashboard. This fixes that to reroute them to the form they were originally trying to submit. I had to work on callback urls to ensure tighter validation so we don’t allow sketchy redirects from people so pls test

What

  • preserved form routes as callback urls when unauthenticated users tried to access forms
    • /forms/[formName]
    • /forms/[formName]/[responseId]
  • included existing query params in callback urls so context isn’t lost
  • updated hero sign-in flow to read callbackURL from search params and use it when valid (forms paths), otherwise fall back
  • added sanitizeCallbackURL in auth package to ensure safe same origin/ path redirects
  • applied callback sanitization in both auth client + server sign in paths
  • encoded provider and callbackURL in the server redirect url
  • added provider guard in signInRoute to only cord
  • switched form pages to use auth from @forge/auth/server

Test Plan

  • make a form on local try to access without auth itll reroute and then prompt user to sign in

Video

Screen.Recording.2026-02-07.at.4.10.19.PM.1.1.mp4

Summary by CodeRabbit

  • New Features
    • Sign-in now returns users to the exact page they came from (including form pages) with query parameters preserved.
  • Bug Fixes
    • Callback URLs are validated/sanitized to prevent unsafe redirects.
    • Sign-in now rejects unsupported providers and ensures redirects use a safe, encoded callback.

@Adr1an04 Adr1an04 added the Minor Small change - 1 reviewer required label Feb 7, 2026
@coderabbitai
Copy link

coderabbitai bot commented Feb 7, 2026

📝 Walkthrough

Walkthrough

Pages and the hero sign-in now construct and pass a callbackURL (including encoded form path and query params) when redirecting unauthenticated users to sign-in. A new sanitizeCallbackURL utility validates/sanitizes callback URLs and is integrated into server/client sign-in flows.

Changes

Cohort / File(s) Summary
Form pages
apps/blade/src/app/forms/[formName]/page.tsx, apps/blade/src/app/forms/[formName]/[responseId]/page.tsx
Added searchParams param handling, serializeSearchParams helper, switched auth import to server variant, and build callbackURL that encodes the form path plus serialized query params for unauthenticated redirects.
Hero sign-in component
apps/blade/src/app/_components/hero.tsx
Added useSearchParams, extract/derive callbackURL from query params, and use that callbackURL (or default) when triggering sign-in redirect.
Callback URL sanitization
packages/auth/src/callback-url.ts
New exported `sanitizeCallbackURL(callbackURL?: string
Auth integration
packages/auth/src/index.rsc.ts, packages/auth/src/index.ts
Integrated sanitizeCallbackURL into sign-in flows (server & client), encoded provider in redirects, and added server-side provider validation to reject unsupported providers.

Sequence Diagram(s)

sequenceDiagram
  participant Browser as Browser/User
  participant Form as App Form Page
  participant Hero as Hero Component
  participant AuthSvc as Auth Package (signInRoute / signIn)
  participant Provider as Auth Provider (Discord)
  participant AppURL as App Origin (env)

  Browser->>Form: GET /forms/:formName[?query]
  alt unauthenticated
    Form->>Form: build callbackURL (encode path + serialize searchParams)
    Form-->>Browser: 302 -> /sign-in?provider=discord&callbackURL=ENC(...)
    Browser->>AuthSvc: GET /sign-in?provider=discord&callbackURL=ENC(...)
    AuthSvc->>AppURL: parse env app URL
    AuthSvc->>AuthSvc: sanitizeCallbackURL(ENC(...)) — ensure same-origin & valid path
    AuthSvc->>Provider: redirect to provider auth flow
    Provider-->>AuthSvc: callback on success
    AuthSvc-->>Browser: redirect to sanitized callbackURL
    Browser->>Form: GET sanitized callbackURL (back to form)
  else authenticated
    Form-->>Browser: render form
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main objective of fixing form URL redirects for unauthenticated users.
Linked Issues check ✅ Passed All code changes implement the requirement to preserve form URLs and query parameters when redirecting unauthenticated users to sign-in.
Out of Scope Changes check ✅ Passed All changes directly support the callback URL preservation functionality and sanitization required for issue #330.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch formredirects

No actionable comments were generated in the recent review. 🎉

🧹 Recent nitpick comments
packages/auth/src/index.ts (1)

31-38: Consider adding provider validation for consistency with the server-side route.

The server-side signInRoute in index.rsc.ts (Lines 49-54) validates that provider === "discord", but this client-side signIn function accepts any provider string. While Better Auth's backend configuration likely restricts available providers anyway, adding the same validation here would provide consistent behavior and clearer error feedback.

💡 Optional refactor for consistency
 export const signIn = async (
   provider: string,
   { redirectTo }: { redirectTo: string },
 ) => {
+  if (provider !== "discord") {
+    throw new Error("Unsupported provider");
+  }
   await authClient.signIn.social({
     provider: provider,
     callbackURL: sanitizeCallbackURL(redirectTo),
   });
 };

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

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
apps/blade/src/app/forms/[formName]/page.tsx (1)

14-32: Consider extracting serializeSearchParams to a shared helper.

This helper is duplicated in the responseId form page; extracting to ~/lib/utils (or another shared module) avoids divergence.

♻️ Example refactor (this file)
-import { extractProcedures } from "~/lib/utils";
+import { extractProcedures, serializeSearchParams } from "~/lib/utils";
@@
-function serializeSearchParams(searchParams: {
-  [key: string]: string | string[] | undefined;
-}) {
-  const params = new URLSearchParams();
-
-  for (const [key, value] of Object.entries(searchParams)) {
-    if (value === undefined) continue;
-    if (Array.isArray(value)) {
-      for (const entry of value) {
-        params.append(key, entry);
-      }
-      continue;
-    }
-    params.set(key, value);
-  }
-
-  const queryString = params.toString();
-  return queryString ? `?${queryString}` : "";
-}

Comment on lines +12 to +17
const searchParams = useSearchParams();
const requestedCallbackURL = searchParams.get("callbackURL");
const callbackURL =
requestedCallbackURL?.startsWith("/forms/") === true
? requestedCallbackURL
: "/dashboard";
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Normalize callbackURL before the /forms/ allowlist check.

startsWith("/forms/") on the raw query value can be bypassed with dot‑segments or encoded separators (e.g., /forms/../dashboard), letting non‑form paths through. Normalize first, then apply the allowlist.

🛠️ Suggested fix
-  const requestedCallbackURL = searchParams.get("callbackURL");
-  const callbackURL =
-    requestedCallbackURL?.startsWith("/forms/") === true
-      ? requestedCallbackURL
-      : "/dashboard";
+  const requestedCallbackURL = searchParams.get("callbackURL");
+  const callbackURL = (() => {
+    if (!requestedCallbackURL) return "/dashboard";
+    try {
+      const url = new URL(requestedCallbackURL, window.location.origin);
+      const normalized = `${url.pathname}${url.search}`;
+      return normalized.startsWith("/forms/") ? normalized : "/dashboard";
+    } catch {
+      return "/dashboard";
+    }
+  })();

Also applies to: 43-46

Copy link
Contributor

@DVidal1205 DVidal1205 left a comment

Choose a reason for hiding this comment

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

lgtm, good work

@DVidal1205 DVidal1205 added this pull request to the merge queue Feb 7, 2026
Merged via the queue into main with commit 8751ca6 Feb 7, 2026
6 checks passed
@DVidal1205 DVidal1205 deleted the formredirects branch February 7, 2026 22:07
alexanderpaolini added a commit that referenced this pull request Feb 8, 2026
commit b4e3785
Author: Dylan Vidal <111909137+DVidal1205@users.noreply.github.com>
Date:   Sat Feb 7 17:50:41 2026 -0500

    [#328] Update PR Workflow (#329)

    Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>

commit 8751ca6
Author: Adrian Osorio Blanchard <osorioadrian04@gmail.com>
Date:   Sat Feb 7 17:07:19 2026 -0500

    [#330] Form URL not signed in redirect (#331)

commit c0f89b4
Author: DGoel1602 <rhygonfn@gmail.com>
Date:   Sat Feb 7 12:23:52 2026 -0500

    chore: remove everything email-queue

commit df04574
Author: DGoel1602 <rhygonfn@gmail.com>
Date:   Fri Feb 6 23:18:52 2026 -0500

    chore: update .env.example

commit ad63134
Author: DGoel1602 <rhygonfn@gmail.com>
Date:   Fri Feb 6 23:15:12 2026 -0500

    fix: add @forge/email to blade

commit e81a138
Author: DGoel1602 <rhygonfn@gmail.com>
Date:   Fri Feb 6 22:56:02 2026 -0500

    fix: add early return in hacker data

commit ce5a375
Author: DGoel1602 <rhygonfn@gmail.com>
Date:   Fri Feb 6 22:48:20 2026 -0500

    fix: format + lint + typecheck

commit b3d2be7
Author: DGoel1602 <rhygonfn@gmail.com>
Date:   Fri Feb 6 22:16:42 2026 -0500

    fix: transition all emailing to listmonk

commit 3a1e0d3
Author: DGoel1602 <rhygonfn@gmail.com>
Date:   Fri Feb 6 13:55:21 2026 -0500

    feat: add email package
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Minor Small change - 1 reviewer required

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Form URL not signed in redirect

2 participants