fullstackhero/dotnet-starter-kit · warning · CustomException

The authenticator code is invalid.

Error message

The authenticator code is invalid.

What it means

VerifyEnrollTwoFactorCommandHandler throws CustomException('The authenticator code is invalid.') with HTTP 400 when VerifyTwoFactorTokenAsync returns false for the (space-stripped) code. The secret was generated, but the supplied 6-digit TOTP did not match within the allowed time window.

Solutions

  1. Wait for a fresh 30-second code and retry immediately
  2. Fix device clock (enable automatic time/NTP sync on the authenticator device)
  3. Re-run the enroll step to get a new QR/secret, then scan and verify with a fresh code
  4. Verify the client strips spaces and sends the 6-digit code exactly (the handler already removes spaces)
  5. Ensure the issuer/digits (6) configured in the otpauth URI match the authenticator app entry

Example fix

// before
await verifyEnroll(codeFromOldQr);
// after
await enrollTwoFactor();   // rotates secret, returns fresh QR
const fresh = await readFreshTotp();
await verifyEnroll(fresh);
Defensive patterns

Strategy: validation

Validate before calling

function validateTotp(code) {
  const clean = (code ?? '').replace(/\s+/g, '');
  return /^\d{6}$/.test(clean) ? clean : null;
}
const clean = validateTotp(userInput);
if (!clean) show('Enter the 6-digit code from your authenticator.');

Try / catch

try {
  await api.verifyEnrollTwoFactor({ code: clean });
} catch (e) {
  if (e.status === 400 && /authenticator code is invalid/i.test(e.message)) {
    attempts++;
    show(attempts >= 3 ? 'Re-scan the QR and try a fresh code.' : 'Wrong code — wait for the next one.');
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: Typing a wrong or old code; device clock skew pushing the TOTP outside the validation window; scanning the QR from a previous (rotated) enrollment; code reuse past the one-time window.

Common situations: User's phone clock is off by minutes; user rescanned an old QR after re-enrolling; autocomplete filled a stale code; user entered the recovery key instead of a TOTP.

Understand the failure class

Related errors


AI-assisted analysis of fullstackhero/dotnet-starter-kit@3f2959e683 (2026-09-15). Data as JSON: /api/errors/4979ca35d946a03c. Report an issue: GitHub.

Appendix: source

Thrown at src/Modules/Identity/Modules.Identity/Features/v1/TwoFactor/VerifyEnroll/VerifyEnrollTwoFactorCommandHandler.cs:44

        if (!_currentUser.IsAuthenticated())
        {
            throw new UnauthorizedException();
        }

        var userId = _currentUser.GetUserId().ToString();
        var user = await _userManager.FindByIdAsync(userId)
            ?? throw new NotFoundException($"User {userId} not found.");

        var sanitized = command.Code.Replace(" ", string.Empty, StringComparison.Ordinal);
        var valid = await _userManager.VerifyTwoFactorTokenAsync(
            user,
            _userManager.Options.Tokens.AuthenticatorTokenProvider,
            sanitized);

        if (!valid)
        {
            throw new CustomException(
                "The authenticator code is invalid.",
                errors: null,
                System.Net.HttpStatusCode.BadRequest);
        }

        await _userManager.SetTwoFactorEnabledAsync(user, true);
        return true;
    }
}

View on GitHub (pinned to 3f2959e683)