Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

Satellite domains cannot be tested when using ports on domains

Open
#8,263 0 comments 1 reaction 0 assignees View on GitHub

Maintainers usually reply within 1 day

Nobody has claimed this yet.

  • #9059 by @FrancoKaddour — closed without merging

Assessment

Difficulty
3/5
Estimated time
1-2 days
Newbie friendliness
25/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Quiet
Tech stack
typescript

Research direction

Start in packages/shared/src/internal/clerk-js/url.ts at isAllowedRedirect and review the reported reproduction for satellite redirects using non-default ports. Run or add focused tests covering wildcard origins and explicit ports, then verify that valid non-default-port redirects are accepted without weakening domain matching; the issue also mentions a changeset and an existing PR branch.

Written by the indexing model from the issue text.

Description

needs-triage
Preliminary Checks
Reproduction

https://github.com/jeffreytgilbert/javascript/tree/fix/allowed-redirect-origins-non-default-ports

Publishable key

pk_test_aW50aW1hdGUtdXJjaGluLTc1LmNsZXJrLmFjY291bnRzLmRldiQ

Description

Summary

When using Clerk with custom local dev domains (e.g. Vite dev servers on :5173, :5174, :5176), the Account Portal ignores the redirect_url parameter and falls back to the configured Home URL. The root cause is in isAllowedRedirect in @clerk/shared: glob patterns like https://*.example.com never match origins that include a non-default port (https://sub.example.com:5176), so the check always fails and the warning fires.


Affected packages

  • @clerk/shared — isAllowedRedirect in src/internal/clerk-js/url.ts

Steps to reproduce

  1. Set up any Clerk satellite app running on a custom local domain with a non-default port — e.g. https://app.admin.example.net:5174 (a typical Vite dev server).
  2. Use an auth passthrough pattern where the Account Portal's redirect_url points to another custom-domain dev server with a port — e.g. https://app.auth.example.net:5176/?target=....
  3. Sign in on the Account Portal (intimate-urchin-75.accounts.dev or equivalent dev instance).
  4. Observe that the browser does not redirect to https://app.auth.example.net:5176/?target=... — it goes to the Home URL configured in the Clerk Dashboard instead.

The browser console on the Account Portal shows:

Clerk: Redirect URL https://app.auth.example.net:5176/?target=... is not on one of the
allowedRedirectOrigins, falling back to the default redirect URL.

This occurs even though https://*.example.net is one of the origins in the generated allowedRedirectOrigins list (via createAllowedRedirectOrigins), and even if the user explicitly adds https://app.auth.example.net:5176 to the allowedRedirectOrigins prop on their ClerkProvider.


Expected behavior

https://*.example.net should match https://app.auth.example.net:5176. A pattern that covers a domain should cover that domain on any port, since non-default ports only appear in local dev environments — in production, HTTPS traffic is always on port 443 and url.port is the empty string.


Actual behavior

The match always fails for any URL with a non-default port when the pattern is a string. The warning fires and Clerk redirects to the Home URL, breaking the redirect_url flow entirely.


Root cause

isAllowedRedirect converts string entries in allowedRedirectOrigins to regexes using glob-to-regexp, then tests them against url.origin:

// packages/shared/src/internal/clerk-js/url.ts
const isAllowed =
  !isProblematicUrl(url) &&
  (isSameOrigin ||
    allowedRedirectOrigins
      .map(origin => (typeof origin === 'string' ? globs.toRegexp(trimTrailingSlash(origin)) : origin))
      .some(origin => origin.test(trimTrailingSlash(url.origin))));

glob-to-regexp converts https://*.example.net to a regex roughly equivalent to /^https:\/\/[^/]*\.example\.net$/. When url.origin is https://app.auth.example.net:5176, the trailing :5176 causes the match to fail — the regex is anchored to .net$ and the port breaks it.

This also means that explicitly adding 'https://app.auth.example.net:5176' to allowedRedirectOrigins in the satellite's ClerkProvider does not help, because that prop controls validation within the satellite app's own SDK instance — it has no effect on the Account Portal's isAllowedRedirect check, which uses the origins generated by createAllowedRedirectOrigins (derived from the instance's FAPI configuration).

The default origins generated by createAllowedRedirectOrigins for a dev instance are:

https://jurying.net          ← no port
https://*.jurying.net        ← no port, glob fails on :5176
https://foo-bar-42.clerk.accounts.dev

None of these match a URL with a non-default port.


Proposed fix

After the full-origin test fails, also test each pattern against the port-stripped origin (protocol + hostname only) when the redirect URL has an explicit non-default port. url.port is an empty string for default ports (443 for https, 80 for http), so this fallback is only reached in non-standard-port contexts. The domain must still satisfy the pattern — only the port check is relaxed.

const patterns = allowedRedirectOrigins.map(origin =>
  typeof origin === 'string' ? globs.toRegexp(trimTrailingSlash(origin)) : origin,
);

const portlessOrigin = url.port ? `${url.protocol}//${url.hostname}` : null;

const isAllowed =
  !isProblematicUrl(url) &&
  (isSameOrigin ||
    patterns.some(
      pattern =>
        pattern.test(trimTrailingSlash(url.origin)) ||
        (portlessOrigin !== null && pattern.test(portlessOrigin)),
    ));

A PR with this fix, tests, and a changeset is available at: https://github.com/jeffreytgilbert/javascript/tree/fix/allowed-redirect-origins-non-default-ports


Why the ClerkProvider.allowedRedirectOrigins prop doesn't help

Worth calling out explicitly because it's the obvious first thing people try. The allowedRedirectOrigins prop on a satellite's ClerkProvider only gates redirects within that satellite app's own SDK context. The Account Portal is Clerk's hosted application — you cannot pass props to its ClerkProvider. Its allowedRedirectOrigins list comes from createAllowedRedirectOrigins, which uses the Clerk instance's FAPI-configured origins. No amount of configuring the satellite's ClerkProvider affects what the Account Portal validates.


Environment

  • @clerk/shared: 4.4.0 (confirmed in source — behaviour is present in current main)
  • Trigger context: Account Portal → satellite redirect in local dev with Vite (non-standard port)
  • Reproduces with both the default origins from createAllowedRedirectOrigins and user-supplied string entries in allowedRedirectOrigins
Environment
System:
    OS: macOS 26.4
    CPU: (10) arm64 Apple M1 Max
    Memory: 2.87 GB / 64.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 24.14.1 - /Users/jeffreygilbert/.nvm/versions/node/v24.14.1/bin/node
    npm: 11.11.0 - /Users/jeffreygilbert/.nvm/versions/node/v24.14.1/bin/npm
    pnpm: 10.33.0 - /opt/homebrew/bin/pnpm
    bun: 1.3.11 - /opt/homebrew/bin/bun
  Browsers:
    Brave Browser: 146.1.88.136
    Chrome: 146.0.7680.178
    Firefox: 148.0.2
    Safari: 26.4
    Safari Technology Preview: 26.0
Dominant language
TypeScript
Stars
1.8k
Forks
479
Avg merge
1d 18h
Merged PRs (30d)
313

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from clerk/javascript

All issues in clerk/javascript

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.