Skip to content

<SignIn> sends a new email OTP on every mount/refresh while a pending verification already exists #8463

Closed

Description

@marekabaffy

Reproduction

  1. Render <SignIn withSignUp /> on a route.
  2. Submit an email address. Clerk sends an email_code OTP. The OTP screen appears with a "Resend" button on a 30s cooldown.
  3. Hard-refresh the page (or, on a mobile WebView, background the app long enough for the OS to kill it, then re-open).
  4. The OTP screen re-renders. A second OTP email arrives within seconds.
  5. Refreshing again sends a third. And so on — the 30s cooldown is never respected across mounts.

Network panel shows a fresh POST .../sign_ins/{id}/prepare_first_factor request firing on every mount, even though client.signIn?.status === 'needs_first_factor' is already true at mount time with a valid pending verification.

Expected behavior

On mount, when client.signIn?.status === 'needs_first_factor' (or the equivalent for signUp) and email_code is the active strategy, <SignIn> should resume on the existing verification rather than calling prepareFirstFactor again. A new code should only be sent when the user explicitly clicks "Resend" (and only after the cooldown window).

The client.signIn resource already persists this state server-side per client — the prebuilt component just isn't reading it before re-preparing.

Why this matters in practice

Web impact is mild: most users don't refresh during OTP entry. The painful case is mobile apps that embed <SignIn> in a WebView:

  1. User is shown the OTP screen.
  2. User backgrounds the app to open their email client.
  3. The OS kills the WebView host process to reclaim memory.
  4. User returns; the app cold-starts; <SignIn> mounts fresh; a new OTP is sent.
  5. By the time the user reads the first email (the one they actually went to fetch), it's already superseded.

This makes WebView-embedded sign-in unusable on memory-constrained devices.

Environment

  • @clerk/clerk-react: 5.61.5
  • @clerk/react-router: 1.10.2
  • @clerk/shared: 3.47.4
  • @clerk/types: 4.101.22
  • React: 19, Vite 5
  • Reproduces on a pk_test_* instance with email_code + Google + Apple enabled, withSignUp mode
  • Reproduces in Chrome (desktop) and in iOS/Android WKWebView/WebView shells

Suggested fix

Inside <SignIn>'s mount effect for the verification step, branch on the existing client.signIn state before calling prepareFirstFactor:

if (
  client.signIn?.status === 'needs_first_factor' &&
  client.signIn.firstFactorVerification?.strategy === 'email_code' &&
  client.signIn.firstFactorVerification?.status !== 'expired'
) {
  // resume — do NOT call prepareFirstFactor
  return;
}

Same logic for <SignUp> reading client.signUp.verifications.emailAddress.

Activity

  1. bulam commented on May 10, 2026

    @bulam

    A fix for this would be greatly appreciated.

  2. self-assigned this
    on May 15, 2026
  3. titusdecali commented on Jun 13, 2026

    @titusdecali

    I'm still seeing this in the latest version. Has this really been fixed?

  4. KnowhereFern commented on Jun 15, 2026

    @KnowhereFern

    Chiming in that I am also having this issue in production and I found out the hard way lol

  5. jacekradko commented on Jun 15, 2026

    @jacekradko
    Contributor

    @titusdecali @KnowhereFern could you confirm which Clerk SDK version you're running? Specifically, are you on @clerk/clerk-react@5.x (Core 2) or @clerk/react@6.x (Core 3)? The fix here shipped in the Core 3 line, so that will tell us whether you're missing it or hitting a separate case.

  6. titusdecali commented on Jun 16, 2026

    @titusdecali

    @jacekradko

    could you confirm which Clerk SDK version you're running?

    Thanks. To clarify, we're not on the React SDK. This is a Nuxt app on the official @clerk/nuxt module:

    @clerk/nuxt 2.6.2 (latest stable 2.6.3) → @clerk/vue 2.4.2, @clerk/shared 4.17.1, @clerk/backend 3.7.0

    At runtime this loads @clerk/clerk-js@6 → window.Clerk.version = 6.17.0 (current stable v6).

    We use the prebuilt component (no custom useSignIn() flow). The duplicate email_code fires when the factor-one step remounts/refreshes.

    So we're already on stable clerk-js v6, yet still seeing the double code, and I can't find #8548 in the stable clerk-js changelog. Which @clerk/clerk-js version contains the SignInFactorOneCodeForm re-prepare fix? If it's a published tag, I can pin it via clerkJsVersion without changing SDKs. If it only lives in the Core 3 line, what's the path for Vue/Nuxt?

    The newest canary-core3 publish for @clerk/nuxt/@clerk/vue is from Dec 8 2025, which predates the fix, and there's no stable @clerk/nuxt@3.

  7. KnowhereFern commented on Jun 19, 2026

    @KnowhereFern

    @jacekradko confirming that I'm on @clerk/clerk-react@5.61.8 (Core 2). Upgrading to three now

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions