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
- Wait for a fresh 30-second code and retry immediately
- Fix device clock (enable automatic time/NTP sync on the authenticator device)
- Re-run the enroll step to get a new QR/secret, then scan and verify with a fresh code
- Verify the client strips spaces and sends the 6-digit code exactly (the handler already removes spaces)
- 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
- Enable automatic time sync (NTP) on the user's device
- Always re-enroll (fresh QR) after a failed or abandoned enrollment
- Strip spaces client-side and enforce 6 digits before submitting
- Show a live countdown to the next 30-second TOTP window
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
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- two_factor_required: An authenticator code is required to…
- two_factor_invalid: The authenticator code is invalid or…
- System role permissions are managed by the framework and…
- Unauthorized
- User not found.
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)