Skip to content

Conversation

@dstaley
Copy link
Member

@dstaley dstaley commented Oct 17, 2025

Description

This PR adds support for modals to the SSO sign-in method in the new APIs.

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

Summary by CodeRabbit

  • New Features
    • Added experimental support for modal-based SSO sign-ins, enabling users to authenticate via popup windows.
    • Enhanced popup flow handling for enterprise SSO sign-in with improved redirect management.

@changeset-bot
Copy link

changeset-bot bot commented Oct 17, 2025

🦋 Changeset detected

Latest commit: 4f56b48

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

This PR includes changesets to release 19 packages
Name Type
@clerk/clerk-js Minor
@clerk/types Minor
@clerk/chrome-extension Patch
@clerk/clerk-expo Patch
@clerk/agent-toolkit Patch
@clerk/astro Patch
@clerk/backend Patch
@clerk/elements Patch
@clerk/expo-passkeys Patch
@clerk/express Patch
@clerk/fastify Patch
@clerk/localizations Patch
@clerk/nextjs Patch
@clerk/nuxt Patch
@clerk/react-router Patch
@clerk/remix Patch
@clerk/tanstack-react-start Patch
@clerk/testing Patch
@clerk/vue 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
Copy link

vercel bot commented Oct 17, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
clerk-js-sandbox Ready Ready Preview Comment Nov 6, 2025 9:45pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 17, 2025

Walkthrough

The changes implement experimental modal SSO sign-in support by removing hard-coded modal flow restrictions, adding popup-aware routing infrastructure, and introducing enterprise SSO preflight behavior. A new changeset documents the version updates.

Changes

Cohort / File(s) Summary
Changeset documentation
.changeset/ripe-banks-pay.md
Documents minor version bumps for @clerk/clerk-js and @clerk/types with an experimental note indicating support for modal SSO sign-ins to new APIs.
SignIn resource SSO flow refactoring
packages/clerk-js/src/core/resources/SignIn.ts
Removes hard-coded modal flow restriction; derives action from redirectCallbackUrl and redirectUrl. Adds popup-aware routing via wrapWithPopupRoutes. For enterprise SSO, performs a prepare_first_factor POST after creation with merged routes, oidcPrompt, and enterpriseConnectionId. Post-creation redirect handling now uses _futureAuthenticateWithPopup for popup cases or normal navigation for non-popup; reloads SignIn resource when externalVerificationRedirectURL exists with popup.
Popup authentication utilities
packages/clerk-js/src/utils/authenticateWithPopup.ts
Adds wrapWithPopupRoutes function to construct /popup-callback redirect and callback URLs with authentication tokens. Adds _futureAuthenticateWithPopup function to handle OAuth flow completion from popup via message listener with event origin validation, session/return_url resolution, and popup navigation.

Sequence Diagram

sequenceDiagram
    participant User
    participant SignIn as SignIn Resource
    participant Popup as Popup Window
    participant OAuth as OAuth Provider
    participant Listener as Message Listener

    rect rgb(220, 240, 255)
    Note over User,Listener: New Popup SSO Flow
    User->>SignIn: Initiate SSO with popup flag
    SignIn->>SignIn: wrapWithPopupRoutes()
    SignIn->>SignIn: Create with wrapped routes
    
    alt Enterprise SSO
        SignIn->>SignIn: prepare_first_factor POST
        SignIn->>SignIn: Merge routes + enterpriseConnectionId
    end
    
    SignIn->>Popup: Open popup window
    Popup->>OAuth: Navigate to external verification URL
    OAuth->>Popup: Complete OAuth flow
    Popup->>Listener: Post message (session/return_url)
    Listener->>SignIn: Handle completion
    SignIn->>SignIn: Reload resource state
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~35 minutes

  • SignIn.ts refactoring: Dense logic changes to SSO flow—removal of modal restrictions, popup routing integration, enterprise SSO preflight, and post-creation redirect handling with state reloading require careful verification of control flow.
  • New popup utilities: _futureAuthenticateWithPopup includes message listener setup, event origin validation, and Promise-based OAuth completion handling—non-trivial async patterns requiring thorough testing consideration.
  • Interconnected changes: The three files work together in the popup flow; understanding the overall interaction model is essential to validate correctness.

Poem

🐰 A popup appears, where SSO takes flight,
Enterprise flows dance with routes held tight,
Messages whisper when OAuth completes,
Modal SSO now knows many treats! 🌟

Pre-merge checks and finishing touches

❌ 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%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding support for modal SSO sign-ins to clerk-js and types packages, which aligns with the primary objective and code changes.
✨ 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 ds.feat/signals-sso-modal

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between d834f6f and 4f56b48.

📒 Files selected for processing (1)
  • packages/clerk-js/src/core/resources/SignIn.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/development.mdc)

**/*.{js,jsx,ts,tsx}: All code must pass ESLint checks with the project's configuration
Follow established naming conventions (PascalCase for components, camelCase for variables)
Maintain comprehensive JSDoc comments for public APIs
Use dynamic imports for optional features
All public APIs must be documented with JSDoc
Provide meaningful error messages to developers
Include error recovery suggestions where applicable
Log errors appropriately for debugging
Lazy load components and features when possible
Implement proper caching strategies
Use efficient data structures and algorithms
Profile and optimize critical paths
Validate all inputs and sanitize outputs
Implement proper logging with different levels

Files:

  • packages/clerk-js/src/core/resources/SignIn.ts
**/*.{js,jsx,ts,tsx,json,css,scss,md,yaml,yml}

📄 CodeRabbit inference engine (.cursor/rules/development.mdc)

Use Prettier for consistent code formatting

Files:

  • packages/clerk-js/src/core/resources/SignIn.ts
packages/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/development.mdc)

TypeScript is required for all packages

Files:

  • packages/clerk-js/src/core/resources/SignIn.ts
packages/**/*.{ts,tsx,d.ts}

📄 CodeRabbit inference engine (.cursor/rules/development.mdc)

Packages should export TypeScript types alongside runtime code

Files:

  • packages/clerk-js/src/core/resources/SignIn.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/development.mdc)

Use proper TypeScript error types

**/*.{ts,tsx}: Always define explicit return types for functions, especially public APIs
Use proper type annotations for variables and parameters where inference isn't clear
Avoid any type - prefer unknown when type is uncertain, then narrow with type guards
Use interface for object shapes that might be extended
Use type for unions, primitives, and computed types
Prefer readonly properties for immutable data structures
Use private for internal implementation details
Use protected for inheritance hierarchies
Use public explicitly for clarity in public APIs
Prefer readonly for properties that shouldn't change after construction
Prefer composition and interfaces over deep inheritance chains
Use mixins for shared behavior across unrelated classes
Implement dependency injection for loose coupling
Let TypeScript infer when types are obvious
Use const assertions for literal types: as const
Use satisfies operator for type checking without widening
Use mapped types for transforming object types
Use conditional types for type-level logic
Leverage template literal types for string manipulation
Use ES6 imports/exports consistently
Use default exports sparingly, prefer named exports
Use type-only imports: import type { ... } from ...
No any types without justification
Proper error handling with typed errors
Consistent use of readonly for immutable data
Proper generic constraints
No unused type parameters
Proper use of utility types instead of manual type construction
Type-only imports where possible
Proper tree-shaking friendly exports
No circular dependencies
Efficient type computations (avoid deep recursion)

Files:

  • packages/clerk-js/src/core/resources/SignIn.ts
**/*.{js,ts,tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/monorepo.mdc)

Support multiple Clerk environment variables (CLERK_, NEXT_PUBLIC_CLERK_, etc.) for configuration.

Files:

  • packages/clerk-js/src/core/resources/SignIn.ts
🧬 Code graph analysis (1)
packages/clerk-js/src/core/resources/SignIn.ts (1)
packages/clerk-js/src/utils/authenticateWithPopup.ts (2)
  • wrapWithPopupRoutes (88-120)
  • _futureAuthenticateWithPopup (122-152)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Formatting | Dedupe | Changeset
  • GitHub Check: Build Packages
  • GitHub Check: triage
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: semgrep-cloud-platform/scan
  • GitHub Check: semgrep-cloud-platform/scan

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

@pkg-pr-new
Copy link

pkg-pr-new bot commented Oct 17, 2025

Open in StackBlitz

@clerk/agent-toolkit

npm i https://pkg.pr.new/@clerk/agent-toolkit@7026

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@7026

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@7026

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@7026

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@7026

@clerk/dev-cli

npm i https://pkg.pr.new/@clerk/dev-cli@7026

@clerk/elements

npm i https://pkg.pr.new/@clerk/elements@7026

@clerk/clerk-expo

npm i https://pkg.pr.new/@clerk/clerk-expo@7026

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@7026

@clerk/express

npm i https://pkg.pr.new/@clerk/express@7026

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@7026

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@7026

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@7026

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@7026

@clerk/clerk-react

npm i https://pkg.pr.new/@clerk/clerk-react@7026

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@7026

@clerk/remix

npm i https://pkg.pr.new/@clerk/remix@7026

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@7026

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@7026

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@7026

@clerk/themes

npm i https://pkg.pr.new/@clerk/themes@7026

@clerk/types

npm i https://pkg.pr.new/@clerk/types@7026

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@7026

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@7026

commit: 4f56b48

Copy link
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: 4

🧹 Nitpick comments (4)
.changeset/ripe-banks-pay.md (1)

6-6: Expand the changeset note with migration guidance.

Call out:

  • Removed: flow and strategy: 'saml' in SignInFutureSSOParams.
  • New: popup, oidcPrompt, enterpriseConnectionId.
    Add short migration examples.
packages/types/src/signInFuture.ts (2)

183-196: Model the popup target with a named interface to allow future enhancements (e.g., detect closed).

Current type is minimal. Consider:

  • Add an exported interface to avoid importing DOM Window.
  • Include optional closed?: boolean for cleanup.

Example:

+export interface PopupLike {
+  readonly location: { href: string };
+  readonly closed?: boolean;
+}
-  popup?: { location: { href: string } };
+  popup?: PopupLike;

197-201: Constrain and document oidcPrompt.

Either:

  • Keep string but enumerate allowed OIDC values in JSDoc with examples for multi-valued inputs, or
  • Introduce a union type (with allowance for provider-specific extensions).

This improves dev ergonomics without blocking edge cases.

packages/clerk-js/src/core/resources/SignIn.ts (1)

927-935: Popup path ignores return_url by design?

In the popup branch you wait for completion and reload the resource, but you do not navigate when return_url is sent (unlike _authenticateWithPopup, which navigates). If the intended modal UX is “stay on page and update state,” this is fine. Otherwise, consider consuming return_url to navigate or provide a callback.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 2a815eb and 66202b7.

📒 Files selected for processing (4)
  • .changeset/ripe-banks-pay.md (1 hunks)
  • packages/clerk-js/src/core/resources/SignIn.ts (2 hunks)
  • packages/clerk-js/src/utils/authenticateWithPopup.ts (1 hunks)
  • packages/types/src/signInFuture.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
.changeset/**

📄 CodeRabbit inference engine (.cursor/rules/monorepo.mdc)

Automated releases must use Changesets.

Files:

  • .changeset/ripe-banks-pay.md
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/development.mdc)

**/*.{js,jsx,ts,tsx}: All code must pass ESLint checks with the project's configuration
Follow established naming conventions (PascalCase for components, camelCase for variables)
Maintain comprehensive JSDoc comments for public APIs
Use dynamic imports for optional features
All public APIs must be documented with JSDoc
Provide meaningful error messages to developers
Include error recovery suggestions where applicable
Log errors appropriately for debugging
Lazy load components and features when possible
Implement proper caching strategies
Use efficient data structures and algorithms
Profile and optimize critical paths
Validate all inputs and sanitize outputs
Implement proper logging with different levels

Files:

  • packages/types/src/signInFuture.ts
  • packages/clerk-js/src/utils/authenticateWithPopup.ts
  • packages/clerk-js/src/core/resources/SignIn.ts
**/*.{js,jsx,ts,tsx,json,css,scss,md,yaml,yml}

📄 CodeRabbit inference engine (.cursor/rules/development.mdc)

Use Prettier for consistent code formatting

Files:

  • packages/types/src/signInFuture.ts
  • packages/clerk-js/src/utils/authenticateWithPopup.ts
  • packages/clerk-js/src/core/resources/SignIn.ts
packages/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/development.mdc)

TypeScript is required for all packages

Files:

  • packages/types/src/signInFuture.ts
  • packages/clerk-js/src/utils/authenticateWithPopup.ts
  • packages/clerk-js/src/core/resources/SignIn.ts
packages/**/*.{ts,tsx,d.ts}

📄 CodeRabbit inference engine (.cursor/rules/development.mdc)

Packages should export TypeScript types alongside runtime code

Files:

  • packages/types/src/signInFuture.ts
  • packages/clerk-js/src/utils/authenticateWithPopup.ts
  • packages/clerk-js/src/core/resources/SignIn.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/development.mdc)

Use proper TypeScript error types

**/*.{ts,tsx}: Always define explicit return types for functions, especially public APIs
Use proper type annotations for variables and parameters where inference isn't clear
Avoid any type - prefer unknown when type is uncertain, then narrow with type guards
Use interface for object shapes that might be extended
Use type for unions, primitives, and computed types
Prefer readonly properties for immutable data structures
Use private for internal implementation details
Use protected for inheritance hierarchies
Use public explicitly for clarity in public APIs
Prefer readonly for properties that shouldn't change after construction
Prefer composition and interfaces over deep inheritance chains
Use mixins for shared behavior across unrelated classes
Implement dependency injection for loose coupling
Let TypeScript infer when types are obvious
Use const assertions for literal types: as const
Use satisfies operator for type checking without widening
Use mapped types for transforming object types
Use conditional types for type-level logic
Leverage template literal types for string manipulation
Use ES6 imports/exports consistently
Use default exports sparingly, prefer named exports
Use type-only imports: import type { ... } from ...
No any types without justification
Proper error handling with typed errors
Consistent use of readonly for immutable data
Proper generic constraints
No unused type parameters
Proper use of utility types instead of manual type construction
Type-only imports where possible
Proper tree-shaking friendly exports
No circular dependencies
Efficient type computations (avoid deep recursion)

Files:

  • packages/types/src/signInFuture.ts
  • packages/clerk-js/src/utils/authenticateWithPopup.ts
  • packages/clerk-js/src/core/resources/SignIn.ts
**/*.{js,ts,tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/monorepo.mdc)

Support multiple Clerk environment variables (CLERK_, NEXT_PUBLIC_CLERK_, etc.) for configuration.

Files:

  • packages/types/src/signInFuture.ts
  • packages/clerk-js/src/utils/authenticateWithPopup.ts
  • packages/clerk-js/src/core/resources/SignIn.ts
🧬 Code graph analysis (2)
packages/clerk-js/src/utils/authenticateWithPopup.ts (2)
packages/types/src/clerk.ts (1)
  • Clerk (165-896)
packages/shared/src/buildAccountsBaseUrl.ts (1)
  • buildAccountsBaseUrl (4-15)
packages/clerk-js/src/core/resources/SignIn.ts (3)
packages/clerk-js/src/utils/runAsyncResourceTask.ts (1)
  • runAsyncResourceTask (8-31)
packages/clerk-js/src/utils/authenticateWithPopup.ts (2)
  • wrapWithPopupRoutes (88-120)
  • _futureAuthenticateWithPopup (122-151)
packages/clerk-js/src/utils/windowNavigate.ts (1)
  • windowNavigate (19-23)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Build Packages
  • GitHub Check: Formatting | Dedupe | Changeset
  • GitHub Check: semgrep-cloud-platform/scan
  • GitHub Check: semgrep-cloud-platform/scan
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (1)
packages/types/src/signInFuture.ts (1)

170-176: Remove incorrect claim about 'flow' deprecation; clarify 'saml' removal from SignInFutureSSOParams is intentional.

The removal of 'saml' from SignInFutureSSOParams.strategy (line 173) is intentional and aligns with the @deprecated marking on SamlStrategy in packages/types/src/strategies.ts. Users should migrate to 'enterprise_sso' for SSO flows. However, this change should be documented here or in clerk-docs with clear migration guidance, especially since 'saml' remains supported in SignInFutureCreateParams (line 17).

The claim about 'flow' deprecation is incorrect—SignInFuturePasskeyParams.flow (line 244+) has no deprecation marker and appears unchanged. Remove this from the concern.

Comment on lines +2 to +6
'@clerk/clerk-js': minor
'@clerk/types': minor
---

[Experimental] Add support for modal SSO sign-ins to new APIs
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Semver: this includes breaking type changes; minor bump is insufficient for @clerk/types.

SignInFutureSSOParams removes flow and drops 'saml' from strategy. That breaks user code compiling against @clerk/types. Please bump @clerk/types as a major (and consider whether @clerk/clerk-js also needs a major if runtime behavior changes), or gate these under experimental types with a pre-release tag.


🏁 Script executed:

#!/bin/bash
# Locate and examine SignInFutureSSOParams type definition
rg -nC5 'SignInFutureSSOParams' --type=ts --type=tsx

Length of output: 27


🏁 Script executed:

#!/bin/bash
# Search for flow property removals in sign-in params
rg -nC3 'flow.*saml' --type=ts --type=tsx

Length of output: 27


🏁 Script executed:

#!/bin/bash
# Find type definitions in @clerk/types package
fd -e ts -e tsx '@clerk/types' | head -20

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Look for strategy type definition
rg -nC5 "strategy.*'saml'" --type=ts --type=tsx

Length of output: 27


🏁 Script executed:

#!/bin/bash
# Search for SignInFutureSSOParams without type filter
rg -nC5 'SignInFutureSSOParams'

Length of output: 3074


🏁 Script executed:

#!/bin/bash
# Find packages directory structure
fd -type d 'types' | head -10

Length of output: 170


🏁 Script executed:

#!/bin/bash
# Look for .ts files in packages
fd -type f -name '*.ts' | grep -i 'sign\|param' | head -20

Length of output: 170


🏁 Script executed:

#!/bin/bash
# Search for strategy union with saml
rg -nC3 "strategy" | grep -i saml

Length of output: 9531


🏁 Script executed:

#!/bin/bash
# Find the changeset file to understand PR scope
cat .changeset/ripe-banks-pay.md

Length of output: 117


🏁 Script executed:

#!/bin/bash
# Get full SignInFutureSSOParams interface definition
sed -n '169,190p' packages/types/src/signInFuture.ts

Length of output: 664


🏁 Script executed:

#!/bin/bash
# Get context around line 17 to identify the type
sed -n '1,30p' packages/types/src/signInFuture.ts

Length of output: 1466


🏁 Script executed:

#!/bin/bash
# Show git diff for signInFuture.ts to see what changed
git diff HEAD~1 packages/types/src/signInFuture.ts | head -100

Length of output: 1677


🏁 Script executed:

#!/bin/bash
# Check if experimental tag/flag is used in the file
rg -n 'experimental|@experimental' packages/types/src/signInFuture.ts

Length of output: 22


Breaking type changes require major version bump for @clerk/types; current minor classification is incorrect.

The git diff confirms the breaking changes claimed: SignInFutureSSOParams removes the optional flow property and removes 'saml' from the strategy union. Both changes break user code at compile-time or runtime. Only enterpriseConnectionId is marked @experimental; the breaking removals are not gated. Per semver, these require either a major version bump or experimental/pre-release tagging on the breaking properties themselves.

Comment on lines 898 to 906
let routes = { redirectUrl: SignIn.clerk.buildUrlWithAuth(redirectCallbackUrl), actionCompleteRedirectUrl };
if (popup) {
const wrappedRoutes = wrapWithPopupRoutes(SignIn.clerk, {
redirectCallbackUrl: routes.redirectUrl,
redirectUrl: actionCompleteRedirectUrl,
});
routes.redirectUrl = wrappedRoutes.redirectCallbackUrl;
routes.actionCompleteRedirectUrl = wrappedRoutes.redirectUrl;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Ensure routes.redirectUrl is absolute before passing to wrapWithPopupRoutes.

buildUrlWithAuth(redirectCallbackUrl) may return a relative URL; wrapWithPopupRoutes uses new URL(...) which requires an absolute or a base. Normalize here to prevent runtime errors.

-      let routes = { redirectUrl: SignIn.clerk.buildUrlWithAuth(redirectCallbackUrl), actionCompleteRedirectUrl };
+      let routes = {
+        redirectUrl: (() => {
+          const withAuth = SignIn.clerk.buildUrlWithAuth(redirectCallbackUrl);
+          try {
+            // Will succeed if absolute
+            new URL(withAuth);
+            return withAuth;
+          } catch {
+            return window.location.origin + withAuth;
+          }
+        })(),
+        actionCompleteRedirectUrl,
+      };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let routes = { redirectUrl: SignIn.clerk.buildUrlWithAuth(redirectCallbackUrl), actionCompleteRedirectUrl };
if (popup) {
const wrappedRoutes = wrapWithPopupRoutes(SignIn.clerk, {
redirectCallbackUrl: routes.redirectUrl,
redirectUrl: actionCompleteRedirectUrl,
});
routes.redirectUrl = wrappedRoutes.redirectCallbackUrl;
routes.actionCompleteRedirectUrl = wrappedRoutes.redirectUrl;
}
let routes = {
redirectUrl: (() => {
const withAuth = SignIn.clerk.buildUrlWithAuth(redirectCallbackUrl);
try {
// Will succeed if absolute
new URL(withAuth);
return withAuth;
} catch {
return window.location.origin + withAuth;
}
})(),
actionCompleteRedirectUrl,
};
if (popup) {
const wrappedRoutes = wrapWithPopupRoutes(SignIn.clerk, {
redirectCallbackUrl: routes.redirectUrl,
redirectUrl: actionCompleteRedirectUrl,
});
routes.redirectUrl = wrappedRoutes.redirectCallbackUrl;
routes.actionCompleteRedirectUrl = wrappedRoutes.redirectUrl;
}
🤖 Prompt for AI Agents
In packages/clerk-js/src/core/resources/SignIn.ts around lines 898 to 906,
ensure routes.redirectUrl is an absolute URL before passing it to
wrapWithPopupRoutes by detecting if it is relative and resolving it against a
known base (e.g. SignIn.clerk.frontendApi or window.location.origin) using new
URL(relativeOrAbsolute, base). Replace routes.redirectUrl with the resolved
absolute URL (and then call wrapWithPopupRoutes as before), so
wrapWithPopupRoutes always receives an absolute URL and avoids new URL runtime
errors.

Comment on lines 122 to 151
export function _futureAuthenticateWithPopup(
client: Clerk,
params: { popup: { location: { href: string } }; externalVerificationRedirectURL: URL },
): Promise<void> {
return new Promise((resolve, reject) => {
if (!client.client || !params.popup) {
reject();
}

const messageHandler = async (event: MessageEvent) => {
if (event.origin !== buildAccountsBaseUrl(client.frontendApi)) {
return;
}

// The OAuth flow was successful, and we received a message with either a session or a return URL.
if (event.data.session || event.data.return_url) {
window.removeEventListener('message', messageHandler);
resolve();
} else {
reject();
}
};

// Listen for messages from the popup window.
window.addEventListener('message', messageHandler);

// Navigate the popup window to the external verification redirect URL, which kicks off the OAuth flow.
params.popup.location.href = params.externalVerificationRedirectURL.toString();
});
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fix Promise rejection/control-flow and ensure listener cleanup.

  • reject() is called without returning; code continues to add listeners and navigate.
  • Listener isn’t removed on reject paths; potential leak.
  • Provide an error message for callers.

Apply:

 export function _futureAuthenticateWithPopup(
   client: Clerk,
   params: { popup: { location: { href: string } }; externalVerificationRedirectURL: URL },
 ): Promise<void> {
   return new Promise((resolve, reject) => {
-    if (!client.client || !params.popup) {
-      reject();
-    }
+    if (!client.client || !params.popup) {
+      return reject(new Error('Popup auth requires an initialized client and a popup window.'));
+    }

-    const messageHandler = async (event: MessageEvent) => {
-      if (event.origin !== buildAccountsBaseUrl(client.frontendApi)) {
+    const apOrigin = buildAccountsBaseUrl(client.frontendApi);
+    const cleanup = () => window.removeEventListener('message', messageHandler);
+    const messageHandler = (event: MessageEvent) => {
+      if (event.origin !== apOrigin) {
         return;
       }
 
       // The OAuth flow was successful, and we received a message with either a session or a return URL.
       if (event.data.session || event.data.return_url) {
-        window.removeEventListener('message', messageHandler);
-        resolve();
+        cleanup();
+        return resolve();
       } else {
-        reject();
+        cleanup();
+        return reject(new Error('Popup auth did not return a session or return_url.'));
       }
     };
 
     // Listen for messages from the popup window.
     window.addEventListener('message', messageHandler);
 
     // Navigate the popup window to the external verification redirect URL, which kicks off the OAuth flow.
     params.popup.location.href = params.externalVerificationRedirectURL.toString();
   });
 }

Optional: add a timeout to reject if no message is received within N seconds.

🤖 Prompt for AI Agents
In packages/clerk-js/src/utils/authenticateWithPopup.ts around lines 122-151,
the Promise rejects without returning, the message listener isn’t removed on
reject paths, and no error is provided to callers; update the control flow so
that after the initial validation failure you call reject(new Error(...)) and
immediately return; inside the messageHandler, always removeEventListener before
calling resolve() or reject(new Error(...)); if you add an optional timeout,
store its id, clearTimeout upon resolve/reject, and on timeout remove the
listener and reject with a descriptive Error; ensure every early exit path
cleans up the listener and provides an Error message.

@clerk-cookie clerk-cookie removed the types label Nov 6, 2025
@dstaley dstaley merged commit 296fb0b into main Nov 7, 2025
70 of 71 checks passed
@dstaley dstaley deleted the ds.feat/signals-sso-modal branch November 7, 2025 04:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants