Skip to content

Email code is submitted twice: MultiDigitCodeInput re-enters updateEditingValue with no in-flight guard #447

Description

@dbeattie71

Entering an email code submits it twice. The first attempt succeeds; the second replays the now-consumed code and comes back 401.

Harmless today only because the first one wins. It is a race, not a redundancy — and attempt_first_factor is not idempotent.

Steps to reproduce

  1. Sign in with email_code.
  2. Enter the six-digit code into MultiDigitCodeInput (typed or autofilled).
  3. Watch the network.

Expected results

One POST /v1/client/sign_ins/{id}/attempt_first_factor.

Actual results

Two, ~350 ms apart, same sign-in and same code:

15:55:06.562  POST /v1/client/sign_ins/sia_XXX/attempt_first_factor  -> 200
15:55:06.916  POST /v1/client/sign_ins/sia_XXX/attempt_first_factor  -> 401

Cause

MultiDigitCodeInput.updateEditingValue submits whenever the value reaches full length, with no guard against being called again while a submit is in flight:

@override
Future<void> updateEditingValue(TextEditingValue value) async {
  setState(() => _editingValue = value);
  if (value.text.length == widget.length) {
    setState(() => loading = true);
    final succeeded = await widget.onSubmit(value.text);
    …
  }
  …
}

updateEditingValue is the TextInputClient callback, so the platform decides how often it fires. A second delivery carrying the same full-length text — which iOS produces routinely, and more so with autofillHints: [AutofillHints.oneTimeCode] set on this field — re-enters and submits again.

loading is set but never read before submitting, so it styles the widget rather than guarding it.

Suggested fix

Consult the in-flight state before submitting:

if (value.text.length == widget.length && !loading) {

or track submission separately from the display flag if loading is wanted for other reasons.

Why it matters beyond the wasted request

The second attempt increments attempts on the verification, so a flow that allows N tries silently gets N/2. And if the two ever land out of order — plausible under load, since they are concurrent — the 401 could be the one the UI observes, failing a sign-in that actually succeeded.

Code sample

Code sample
// No app code required — this is the SDK's own sign-in panel.
const ClerkAuthentication()

Logs

Logs

Captured with an HttpService decorator; identifiers redacted.

CLERKHTTP POST /v1/client/sign_ins/sia_XXX/attempt_first_factor -> 200
CLERKHTTP POST /v1/client/sign_ins/sia_XXX/attempt_first_factor -> 401

Flutter Doctor output

Doctor output
[✓] Flutter (Channel stable, 3.44.8, on macOS 26.6 25G72 darwin-arm64, locale en-US) [461ms]
    • Flutter version 3.44.8 on channel stable at /opt/homebrew/share/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 058e0af2c2 (5 weeks ago), 2026-07-23 10:56:21 -0700
    • Engine revision 0cd610717b
    • Dart version 3.12.2
    • DevTools version 2.57.0
    • Feature flags: enable-web, enable-linux-desktop, enable-macos-desktop, enable-windows-desktop, enable-android, enable-ios, cli-animations, enable-native-assets, enable-swift-package-manager, omit-legacy-version-file, enable-lldb-debugging, enable-uiscene-migration
[✗] Android toolchain - develop for Android devices [411ms]
    ✗ Unable to locate Android SDK.
      Install Android Studio from: https://developer.android.com/studio/index.html
      On first launch it will assist you in installing the Android SDK components.
      (or visit https://flutter.dev/to/macos-android-setup for detailed instructions).
      If the Android SDK has been installed to a custom location, please use
      `flutter config --android-sdk` to update to that location.
[✓] Xcode - develop for iOS and macOS (Xcode 26.6) [952ms]
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Build 17F113
    • CocoaPods version 1.17.0
[✓] Chrome - develop for the web [4ms]
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
[✓] Connected device (4 available) [6.3s]
    • My iPhone (mobile)    • <redacted>            • ios            • iOS 26.6 23G71
    • iPhone 17 Pro Max (mobile) • <redacted> • ios            • com.apple.CoreSimulator.SimRuntime.iOS-26-5 (simulator)
    • macOS (desktop)            • macos                                • darwin-arm64   • macOS 26.6 25G72 darwin-arm64
    • Chrome (web)               • chrome                               • web-javascript • Google Chrome 151.0.7922.71
[✓] Network resources [403ms]
    • All expected network resources are available.
! Doctor found issues in 1 category.

Activity

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

Metadata

Metadata

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