Skip to content

fix(auth): prevent open redirect vulnerability on sign-in callback#409

Merged
Junman140 merged 1 commit into
Pi-Defi-world:devfrom
godamongstmen897:fix/auth-open-redirect
May 29, 2026
Merged

fix(auth): prevent open redirect vulnerability on sign-in callback#409
Junman140 merged 1 commit into
Pi-Defi-world:devfrom
godamongstmen897:fix/auth-open-redirect

Conversation

@godamongstmen897
Copy link
Copy Markdown
Contributor

@godamongstmen897 godamongstmen897 commented May 27, 2026

This pull request introduces a secure redirect mechanism to prevent open redirect vulnerabilities during authentication flows. The main changes add a utility function to validate redirect URLs, update the sign-in and 2FA flows to use this validation, and include tests to ensure correct behavior.

Security Improvements:

  • Added a new isSafeRedirect utility in lib/redirect.ts to validate and sanitize redirect URLs, only allowing same-origin and relative paths, and rejecting external, protocol-relative, or malformed URLs.
  • Updated the sign-in (app/auth/signin/page.tsx) and 2FA (app/auth/2fa/page.tsx) flows to use isSafeRedirect before redirecting users, ensuring that only validated URLs are used for post-authentication navigation. [1] [2] [3] [4] [5] [6]

Testing:

  • Added comprehensive tests for isSafeRedirect in lib/__tests__/redirect.test.ts to check acceptance of safe URLs and rejection of unsafe or malformed ones.

Session Management:

  • Introduced the post_auth_redirect session storage key to temporarily store validated redirect targets between authentication steps. [1] [2] [3]

These changes collectively harden the authentication flow against open redirect attacks and improve session-based redirect handling.

Closes #312

Summary by CodeRabbit

  • New Features

    • 2FA verification now honors optional post-authentication redirects stored during sign-in.
    • Sign-in page now supports redirect parameters for seamless post-login navigation to user-specified destinations.
  • Tests

    • Added comprehensive test coverage for redirect validation logic.

Review Change Stack

Copilot AI review requested due to automatic review settings May 27, 2026 15:22
@drips-wave
Copy link
Copy Markdown

drips-wave Bot commented May 27, 2026

@godamongstmen897 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 27, 2026

📝 Walkthrough

Walkthrough

This PR fixes an open redirect vulnerability in the sign-in flow by implementing redirect validation. A new isSafeRedirect utility validates that redirect targets are same-origin and safe, preventing attackers from crafting phishing links. The validator is integrated into the sign-in page to check the redirect query parameter, storing validated targets in sessionStorage for the 2FA flow, which then retrieves and uses the validated redirect upon completion.

Changes

Open Redirect Security Fix

Layer / File(s) Summary
Safe redirect validator library
lib/redirect.ts, lib/__tests__/redirect.test.ts
Introduces isSafeRedirect(target, baseOrigin?) that rejects falsy, protocol-relative (//...), and cross-origin URLs; validates same-origin targets and returns normalized pathname with search and hash. Tests cover relative paths, external URLs, protocol-relative URLs, same-origin conversion, and malformed inputs.
Sign-in redirect validation and sessionStorage persistence
app/auth/signin/page.tsx
Imports isSafeRedirect and reads the redirect query parameter once during sign-in result handling. Validates it and stores the safe target in sessionStorage under post_auth_redirect for the 2FA flow. Routes to wallet-setup, validated redirect, or / fallback on successful non-2FA sign-in. Reformats passcode input label and wrapping.
2FA post-verification redirect handling
app/auth/2fa/page.tsx
Defines POST_AUTH_REDIRECT_KEY constant and imports isSafeRedirect. After 2FA verification, retrieves the stored redirect from sessionStorage, validates it, removes the key, and navigates to the safe target or /.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

  • Pi-Defi-world/acbu-frontend#110: Both PRs modify sessionStorage-based state management in the 2FA/sign-in flow; the retrieved PR introduced challenge_token storage, and this PR adds post_auth_redirect storage and retrieval within the same flow.

Poem

🐰 A safer path for every hop,
Validating redirects, no more flop!
From signin to two-factor's dance,
No phishing links shall get a chance.

🚥 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 clearly and concisely summarizes the main security fix addressing the open redirect vulnerability in the authentication callback flow.
Linked Issues check ✅ Passed The PR comprehensively addresses issue #312 by implementing isSafeRedirect validation in signin and 2FA flows, preventing external redirect URLs via query parameters as required.
Out of Scope Changes check ✅ Passed All changes are directly scoped to the open redirect vulnerability fix: new redirect validation utility, integration into auth flows, tests, and session storage for safe redirects.

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

✨ 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 and usage tips.

Copy link
Copy Markdown

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

Note

Copilot was unable to run its full agentic suite in this review.

Adds an open-redirect mitigation utility and uses it to honor a redirect query parameter through the sign-in (and 2FA) flow, persisting a validated path across the 2FA step via sessionStorage.

Changes:

  • New isSafeRedirect helper that returns a same-origin path or null.
  • Sign-in flow reads redirect query param, validates, and routes to it (storing it for the 2FA continuation).
  • 2FA flow consumes and clears the stored post-auth redirect.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
lib/redirect.ts New helper validating redirect targets are same-origin paths.
lib/tests/redirect.test.ts Vitest unit tests covering allow/deny cases.
app/auth/signin/page.tsx Reads/validates redirect param; stores it for 2FA or navigates immediately; also large indentation reformat of the form JSX.
app/auth/2fa/page.tsx Reads and clears stored post-auth redirect, validates, and routes accordingly.

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

});

it('rejects malformed urls', () => {
expect(isSafeRedirect('\\\not a url', 'http://localhost')).toBeNull();
Comment thread lib/redirect.ts
Comment on lines +3 to +9
if (target.startsWith("//")) return null;
try {
const base = baseOrigin ?? (typeof window !== 'undefined' ? window.location.origin : 'http://localhost');
const url = new URL(target, base);
if (url.origin !== base) return null;
if (!url.pathname.startsWith('/')) return null;
return url.pathname + (url.search || '') + (url.hash || '');
Comment thread app/auth/signin/page.tsx
Comment on lines +70 to +71
const safe = isSafeRedirect(redirectParam);
if (safe) sessionStorage.setItem('post_auth_redirect', safe);
Copy link
Copy Markdown
Contributor

@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)
lib/__tests__/redirect.test.ts (1)

4-26: ⚡ Quick win

Consider adding edge case tests for security completeness.

The current coverage is good for common cases. For a security-critical function, consider adding tests for:

  • null / empty string inputs → null
  • javascript:alert(1)null (XSS vector)
  • data:text/html,...null
Suggested additional tests
     it('rejects malformed urls', () => {
         expect(isSafeRedirect('\\\not a url', 'http://localhost')).toBeNull();
     });
+
+    it('rejects null and empty inputs', () => {
+        expect(isSafeRedirect(null, 'http://localhost')).toBeNull();
+        expect(isSafeRedirect('', 'http://localhost')).toBeNull();
+    });
+
+    it('rejects javascript and data URLs', () => {
+        expect(isSafeRedirect('javascript:alert(1)', 'http://localhost')).toBeNull();
+        expect(isSafeRedirect('data:text/html,<script>alert(1)</script>', 'http://localhost')).toBeNull();
+    });
 });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/__tests__/redirect.test.ts` around lines 4 - 26, Add edge-case tests to
the isSafeRedirect suite: add cases that pass null and empty string to
isSafeRedirect and assert they return null, add a test that passes a javascript:
URL (e.g., "javascript:alert(1)") and asserts null, and add a test that passes a
data: URL (e.g., "data:text/html,...") and asserts null; update the existing
lib/__tests__/redirect.test.ts tests (the describe('isSafeRedirect') block and
its it(...) cases) to include these additional expectations to cover XSS/data
URL vectors and empty input handling.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/auth/signin/page.tsx`:
- Around line 70-71: Replace the hard-coded 'post_auth_redirect' literal with
the shared constant used by the 2FA page: import POST_AUTH_REDIRECT_KEY from the
shared module (e.g., lib/redirect.ts or your constants file) and use
sessionStorage.setItem(POST_AUTH_REDIRECT_KEY, safe) in the block where
isSafeRedirect(redirectParam) is checked; ensure the constant is exported from
the shared file so both the 2FA page and app/auth/signin/page.tsx reference the
same symbol.

---

Nitpick comments:
In `@lib/__tests__/redirect.test.ts`:
- Around line 4-26: Add edge-case tests to the isSafeRedirect suite: add cases
that pass null and empty string to isSafeRedirect and assert they return null,
add a test that passes a javascript: URL (e.g., "javascript:alert(1)") and
asserts null, and add a test that passes a data: URL (e.g.,
"data:text/html,...") and asserts null; update the existing
lib/__tests__/redirect.test.ts tests (the describe('isSafeRedirect') block and
its it(...) cases) to include these additional expectations to cover XSS/data
URL vectors and empty input handling.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5126d48e-5e16-410f-a03a-994fb2a82c18

📥 Commits

Reviewing files that changed from the base of the PR and between 0b65d5e and d52b089.

📒 Files selected for processing (4)
  • app/auth/2fa/page.tsx
  • app/auth/signin/page.tsx
  • lib/__tests__/redirect.test.ts
  • lib/redirect.ts

Comment thread app/auth/signin/page.tsx
Comment on lines +70 to +71
const safe = isSafeRedirect(redirectParam);
if (safe) sessionStorage.setItem('post_auth_redirect', safe);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use a shared constant for the sessionStorage key.

The 2FA page defines POST_AUTH_REDIRECT_KEY = 'post_auth_redirect' but this file uses the string literal directly. If either changes independently, the redirect flow silently breaks.

Proposed fix

Export the constant from a shared location (e.g., lib/redirect.ts or a constants file) and import it in both pages:

// In lib/redirect.ts (add export)
+export const POST_AUTH_REDIRECT_KEY = 'post_auth_redirect';

// In app/auth/signin/page.tsx
-import { isSafeRedirect } from "`@/lib/redirect`";
+import { isSafeRedirect, POST_AUTH_REDIRECT_KEY } from "`@/lib/redirect`";
 ...
-                    if (safe) sessionStorage.setItem('post_auth_redirect', safe);
+                    if (safe) sessionStorage.setItem(POST_AUTH_REDIRECT_KEY, safe);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/auth/signin/page.tsx` around lines 70 - 71, Replace the hard-coded
'post_auth_redirect' literal with the shared constant used by the 2FA page:
import POST_AUTH_REDIRECT_KEY from the shared module (e.g., lib/redirect.ts or
your constants file) and use sessionStorage.setItem(POST_AUTH_REDIRECT_KEY,
safe) in the block where isSafeRedirect(redirectParam) is checked; ensure the
constant is exported from the shared file so both the 2FA page and
app/auth/signin/page.tsx reference the same symbol.

@Junman140 Junman140 merged commit 976cbff into Pi-Defi-world:dev May 29, 2026
1 check passed
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.

Open redirect on sign-in callback – C

3 participants