Skip to content

fix(js): preserve SignUpFuture reference across SSO-to-sign-up transition#9060

Open
FrancoKaddour wants to merge 1 commit into
clerk:mainfrom
FrancoKaddour:fix/signup-future-sso-transfer-stale-ref
Open

fix(js): preserve SignUpFuture reference across SSO-to-sign-up transition#9060
FrancoKaddour wants to merge 1 commit into
clerk:mainfrom
FrancoKaddour:fix/signup-future-sso-transfer-stale-ref

Conversation

@FrancoKaddour

@FrancoKaddour FrancoKaddour commented Jul 1, 2026

Copy link
Copy Markdown

Fixes #8338

When an SSO sign-in transitions to a sign-up flow (e.g. the account does not exist and legal acceptance is required), Client.fromJSON created a brand-new SignUp instance because the existing one had no id while the incoming server data did. The SignUpFuture held by useSignUp() hooks pointed at the stale, id-less instance. Calling signUp.update({ legalAccepted: true }) then sent PATCH /client/sign_ups (no id segment) instead of PATCH /client/sign_ups/{id}, resulting in a 405 error.

Root cause: Client.fromJSON only reuses the existing SignUp instance when this.signUp.id === data.sign_up.id. During SSO transfer the old instance has no id, so the condition was false and a new instance was created — discarding the SignUpFuture reference held by hooks.

Fix: Also update in-place when !this.signUp.id (the instance is still uninitialized). __internal_updateFromJSON populates all fields including id, so the existing SignUpFuture transparently sees the correct id after the transition.

Summary by CodeRabbit

  • Bug Fixes
    • Fixed an issue where sign-up updates could fail after an SSO sign-in transitioned into a sign-up flow.
    • Preserved the active sign-up state across that transition so the correct sign-up identifier is used for subsequent updates.

@changeset-bot

changeset-bot Bot commented Jul 1, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 9cd68a6

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

This PR includes changesets to release 4 packages
Name Type
@clerk/clerk-js Patch
@clerk/chrome-extension Patch
@clerk/electron Patch
@clerk/expo 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: e3a9f34d-ffe7-458b-a126-71ec18fe1e42

📥 Commits

Reviewing files that changed from the base of the PR and between 167447f and 9cd68a6.

📒 Files selected for processing (2)
  • .changeset/fix-signup-future-sso-transfer-stale-ref.md
  • packages/clerk-js/src/core/resources/Client.ts
✅ Files skipped from review due to trivial changes (1)
  • .changeset/fix-signup-future-sso-transfer-stale-ref.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/clerk-js/src/core/resources/Client.ts

📝 Walkthrough

Walkthrough

This PR patches @clerk/clerk-js so that Client.fromJSON reuses an existing SignUp instance in place when it has no id yet, rather than only when ids match, preventing a stale SignUp reference during SSO-to-signup transitions.

Changes

Stale SignUp Reference Fix

Layer / File(s) Summary
In-place SignUp update on missing id
packages/clerk-js/src/core/resources/Client.ts, .changeset/fix-signup-future-sso-transfer-stale-ref.md
Broadens the update condition in fromJSON to reuse the existing SignUp instance when it lacks an id (in addition to id-match), preventing replacement of the instance during the SSO-to-signup transition; adds a patch changeset describing the fix.

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

Related issues: Resolves #8338 — sign-in initiated SSO with legal compliance fails on complete-sign-up because signUp.update() hit the collection endpoint without a sign-up id.

Poem

A rabbit hopped through client code so neat,
Found a SignUp losing its own heartbeat.
No id yet? No need to replace!
Keep the same hare in the very same place. 🐇
Now .update() finds the right address to greet.

🚥 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 matches the main fix: preserving SignUpFuture across the SSO-to-sign-up transition.
Linked Issues check ✅ Passed The code change preserves the existing SignUp instance when it lacks an id, which addresses the reported future-flow endpoint bug in #8338.
Out of Scope Changes check ✅ Passed The PR only includes the targeted SignUp state-preservation fix and a matching changeset, with no obvious unrelated changes.
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.

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
packages/clerk-js/src/core/resources/Client.ts (2)

146-154: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Fix logic correctly addresses the stale SignUp reference bug.

The broadened condition if (data.sign_up && this.signUp instanceof SignUp && (this.signUp.id === data.sign_up.id || !this.signUp.id)) correctly preserves the existing SignUp instance (and thus the SignUpFuture reference) when the current instance has no id yet, matching the fix described in the PR objectives. The downstream path()/isNew() mechanics in Base.ts mean this instance will now correctly route PATCH requests to /client/sign_ups/{id} once the id arrives.

One minor readability nit: BaseResource already exposes a public isNew() helper (return !this.id) that is used elsewhere for the same semantic check (e.g. path()). Using this.signUp.isNew() instead of !this.signUp.id here would be more idiomatic and self-documenting.

♻️ Optional refactor for consistency
-      if (data.sign_up && this.signUp instanceof SignUp && (this.signUp.id === data.sign_up.id || !this.signUp.id)) {
+      if (data.sign_up && this.signUp instanceof SignUp && (this.signUp.id === data.sign_up.id || this.signUp.isNew())) {
🤖 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/clerk-js/src/core/resources/Client.ts` around lines 146 - 154, The
SignUp update branch in Client should use the existing resource helper for the
“no id yet” check instead of reading the id directly. In the conditional around
this.signUp.__internal_updateFromJSON, replace the raw !this.signUp.id part with
this.signUp.isNew() so the logic matches BaseResource semantics and stays
consistent with path() and other resource checks.

141-171: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a regression test for the no-id → id sign-up transition. Existing coverage checks matching ids, but not the case where this.signUp.id is unset and fromJSON() receives a sign_up payload. A unit test asserting the same SignUp instance is reused and its id is populated would lock in the SSO→sign-up flow behavior.

🤖 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/clerk-js/src/core/resources/Client.ts` around lines 141 - 171, Add a
regression test for Client.fromJSON covering the no-id to id sign-up transition:
exercise the branch where this.signUp is an existing SignUp with no id and
data.sign_up has an id, and verify the same SignUp instance is reused rather
than replaced. Assert that __internal_updateFromJSON is effectively applied by
checking the original SignUp reference is preserved and its id is populated
after the call, alongside the existing matching-id coverage.
🤖 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 `@packages/shared/src/internal/clerk-js/url.ts`:
- Around line 449-455: The allowlist check in the URL validation logic is too
permissive because `portlessOrigin` is being matched for every pattern, causing
exact entries in the `isAllowed` flow to accept any port on the same host.
Update the logic in `url.ts` so only explicit wildcard or dev-only allowlist
entries can use the portless fallback, while exact allowlist strings remain
port-sensitive. Keep the fix localized to the `isAllowed` computation and the
`patterns.some(...)` matching path so `https://www.clerk.com` does not
implicitly allow `https://www.clerk.com:3000`.

---

Nitpick comments:
In `@packages/clerk-js/src/core/resources/Client.ts`:
- Around line 146-154: The SignUp update branch in Client should use the
existing resource helper for the “no id yet” check instead of reading the id
directly. In the conditional around this.signUp.__internal_updateFromJSON,
replace the raw !this.signUp.id part with this.signUp.isNew() so the logic
matches BaseResource semantics and stays consistent with path() and other
resource checks.
- Around line 141-171: Add a regression test for Client.fromJSON covering the
no-id to id sign-up transition: exercise the branch where this.signUp is an
existing SignUp with no id and data.sign_up has an id, and verify the same
SignUp instance is reused rather than replaced. Assert that
__internal_updateFromJSON is effectively applied by checking the original SignUp
reference is preserved and its id is populated after the call, alongside the
existing matching-id coverage.
🪄 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: Repository YAML (base), Repository UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: e0af06f2-d013-4b37-b8e8-8e4a7c8c9fc7

📥 Commits

Reviewing files that changed from the base of the PR and between 668b1c8 and 9a32372.

📒 Files selected for processing (5)
  • .changeset/fix-allowed-redirect-origins-port.md
  • .changeset/fix-signup-future-sso-transfer-stale-ref.md
  • packages/clerk-js/src/core/resources/Client.ts
  • packages/shared/src/internal/clerk-js/__tests__/url.test.ts
  • packages/shared/src/internal/clerk-js/url.ts

Comment thread packages/shared/src/internal/clerk-js/url.ts Outdated
…tion

When an SSO sign-in transitions to a sign-up flow (e.g. account does not
exist, legal acceptance required), Client.fromJSON created a brand-new
SignUp instance because the existing one had no id and the incoming data
did. The old SignUpFuture held by useSignUp() hooks pointed at the stale
id-less instance, so subsequent update() calls sent PATCH to
/client/sign_ups instead of /client/sign_ups/{id} and received a 405.

Fix: update the existing SignUp in-place (via __internal_updateFromJSON)
when it has no id yet, which preserves the SignUpFuture reference and
ensures hooks see the correct id after the transition.

Fixes clerk#8338
@FrancoKaddour FrancoKaddour force-pushed the fix/signup-future-sso-transfer-stale-ref branch from 167447f to 9cd68a6 Compare July 2, 2026 13:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant