Skip to content

fix(shared): allow satellite redirect URLs with non-default ports#9059

Open
FrancoKaddour wants to merge 4 commits into
clerk:mainfrom
FrancoKaddour:fix/allowed-redirect-origins-port
Open

fix(shared): allow satellite redirect URLs with non-default ports#9059
FrancoKaddour wants to merge 4 commits into
clerk:mainfrom
FrancoKaddour:fix/allowed-redirect-origins-port

Conversation

@FrancoKaddour

@FrancoKaddour FrancoKaddour commented Jul 1, 2026

Copy link
Copy Markdown

Fixes #8263

In local development, satellite apps often run on non-default ports (e.g. Vite on :5173, :5174). When isAllowedRedirect() evaluates the redirect URL, it compares url.origin — which includes the port suffix https://app.example.net:5173) — against patterns like https://*.example.net generated by createAllowedRedirectOrigins. The glob-to-regexp conversion anchors the pattern at the domain end, so the trailing :5173 breaks the match, causing the Account Portal to silently fall back to the home URL instead of honoring the redirect_url.

Fix: when url.port is non-empty, also test the port-stripped origin protocol + hostname) against each pattern. Domain validation is preserved — the fix only relaxes the port suffix comparison, not the domain check.

Summary by CodeRabbit

  • Bug Fixes
    • Fixed redirect/origin validation so redirect URLs with non-default ports (e.g., https://app.example.net:5173) are no longer incorrectly rejected when they match the allowed origin patterns.
    • Improved allowlist matching to remain correct for wildcard host rules and maintain strict handling for exact host entries.
    • Added test coverage for allowed and disallowed port-based redirect cases to prevent domain validation bypass.

isAllowedRedirect() compares url.origin against allowedRedirectOrigins
patterns. When the redirect URL includes a non-default port (e.g. :5173
from a Vite dev server), url.origin includes the port suffix, while
patterns like 'https://*.example.net' have none — causing the match to
fail and the redirect to fall back to the home URL silently.

Fix: when url.port is non-empty, also test the port-stripped origin
(protocol + hostname) against each pattern. Domain validation is
preserved — only the port suffix is relaxed.

Fixes clerk#8263
@changeset-bot

changeset-bot Bot commented Jul 1, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: e25be4d

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 23 packages
Name Type
@clerk/shared Patch
@clerk/astro Patch
@clerk/backend Patch
@clerk/chrome-extension Patch
@clerk/clerk-js Patch
@clerk/electron Patch
@clerk/expo-passkeys Patch
@clerk/expo Patch
@clerk/express Patch
@clerk/fastify Patch
@clerk/headless Patch
@clerk/hono Patch
@clerk/localizations Patch
@clerk/msw Patch
@clerk/nextjs Patch
@clerk/nuxt Patch
@clerk/react-router Patch
@clerk/react Patch
@clerk/tanstack-react-start Patch
@clerk/testing Patch
@clerk/ui Patch
@clerk/vue Patch
@clerk/swingset Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercel Bot commented Jul 1, 2026

Copy link
Copy Markdown

@FrancoKaddour is attempting to deploy a commit to the Clerk Production Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: b8d9782a-298a-4933-b74e-737b03ef19b3

📥 Commits

Reviewing files that changed from the base of the PR and between eea968c and e25be4d.

📒 Files selected for processing (1)
  • packages/shared/src/internal/clerk-js/url.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/shared/src/internal/clerk-js/url.ts

📝 Walkthrough

Walkthrough

isAllowedRedirect now treats wildcard allowlist entries as matching a redirect URL’s portless origin when the URL includes a non-default port. Tests cover allowed and rejected port cases, and a changeset documents the fix.

Changes

Port-aware redirect origin matching

Layer / File(s) Summary
Port-aware allowlist matching
packages/shared/src/internal/clerk-js/url.ts
isAllowedRedirect keeps the existing problematic-URL and same-origin checks, then also tests wildcard string allowlist entries against a portless origin when the redirect URL includes a port, while exact string entries stay port-sensitive.
Port regression coverage and changeset
packages/shared/src/internal/clerk-js/__tests__/url.test.ts, .changeset/fix-allowed-redirect-origins-port.md
Tests add allowed and rejected non-default-port cases, and a changeset records the redirect-origin validation fix.

Estimated code review effort: 2 (Simple) | ~10 minutes

Sequence Diagram(s)

sequenceDiagram
  participant RedirectURL
  participant isAllowedRedirect
  participant allowedRedirectOrigins

  RedirectURL->>isAllowedRedirect: url with explicit port
  isAllowedRedirect->>isAllowedRedirect: reject problematic URLs
  isAllowedRedirect->>allowedRedirectOrigins: test url.origin against patterns
  allowedRedirectOrigins-->>isAllowedRedirect: no match
  isAllowedRedirect->>allowedRedirectOrigins: test portless origin for wildcard strings
  allowedRedirectOrigins-->>isAllowedRedirect: match found
  isAllowedRedirect-->>RedirectURL: allow redirect
Loading

Suggested reviewers: wobsoriano

Poem

A rabbit hopped through ports galore,
:5173, :5176, and more,
Wildcards now hop with grace,
Exact strings keep their place,
And redirects bound clean ashore. 🐇

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly names the shared redirect-port fix and matches the main change.
Linked Issues check ✅ Passed The code and tests implement the #8263 fix by allowing wildcard redirect origins to match non-default ports while preserving domain checks.
Out of Scope Changes check ✅ Passed The changes stay focused on redirect-origin validation, tests, and the required changeset with no unrelated additions.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

@FrancoKaddour

Copy link
Copy Markdown
Author

Good catch. The portless fallback now only applies when the original entry is a wildcard glob (contains *). Exact string entries like https://www.clerk.com remain port-sensitive — the portless test is skipped entirely for them. Updated the tests to reflect this: ['https://www.clerk.com:3000', ['https://www.clerk.com'], false].

Exact string entries in allowedRedirectOrigins (e.g. https://www.clerk.com)
were also matching ports they never declared because the portless-origin
test ran against all patterns. Port is part of the browser origin, so an
exact entry must remain port-sensitive.

The portless fallback now only applies when the original entry is a glob
(contains '*'), which covers the satellite-app use case where the pattern
is https://*.example.net and the dev server runs on :5173.

@coderabbitai coderabbitai Bot left a comment

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.

🧹 Nitpick comments (1)
packages/shared/src/internal/clerk-js/url.ts (1)

426-454: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add an explicit return type to this exported curried API.

isAllowedRedirect is exported and not a test helper, so the touched function should not rely on inferred return types.

♻️ Proposed typing update
 export const isAllowedRedirect =
-  (allowedRedirectOrigins: Array<string | RegExp> | undefined, currentOrigin: string) => (_url: URL | string) => {
+  (
+    allowedRedirectOrigins: Array<string | RegExp> | undefined,
+    currentOrigin: string,
+  ): ((_url: URL | string) => boolean) =>
+  (_url: URL | string): boolean => {

As per coding guidelines, “Always define explicit return types for functions, especially public APIs.” Based on learnings, exported functions and public APIs in TypeScript files should have explicit return type annotations.

🤖 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 `@packages/shared/src/internal/clerk-js/url.ts` around lines 426 - 454, Add an
explicit return type to the exported curried API isAllowedRedirect so it does
not rely on inference. Update the function signature in the url.ts module to
annotate the outer function and, if needed, the returned predicate type, keeping
the existing behavior of relativeToAbsoluteUrl, isProblematicUrl, and the origin
matching logic unchanged.

Sources: Coding guidelines, Learnings

🤖 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.

Nitpick comments:
In `@packages/shared/src/internal/clerk-js/url.ts`:
- Around line 426-454: Add an explicit return type to the exported curried API
isAllowedRedirect so it does not rely on inference. Update the function
signature in the url.ts module to annotate the outer function and, if needed,
the returned predicate type, keeping the existing behavior of
relativeToAbsoluteUrl, isProblematicUrl, and the origin matching logic
unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: d8f91786-f36b-44f3-be9b-0cb9441f7c45

📥 Commits

Reviewing files that changed from the base of the PR and between ff55231 and eea968c.

📒 Files selected for processing (2)
  • packages/shared/src/internal/clerk-js/__tests__/url.test.ts
  • packages/shared/src/internal/clerk-js/url.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/shared/src/internal/clerk-js/tests/url.test.ts

@FrancoKaddour

Copy link
Copy Markdown
Author

Done. Added explicit return types to both the outer and inner functions of isAllowedRedirect.

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.

Satellite domains cannot be tested when using ports on domains

1 participant