bitwarden/server · error · BadRequestException
Invalid token.
Error message
Invalid token.
What it means
BadRequestException with key 'Token' and message 'Invalid token.' is thrown in PUT /authenticator when _userManager.VerifyTwoFactorTokenAsync fails for the Authenticator provider against model.Token. This means the 6-digit TOTP the user typed does not match the time-based code computed from their registered authenticator secret.
Source
Thrown at src/Api/Auth/Controllers/TwoFactorController.cs:149
public async Task<TwoFactorAuthenticatorUpdateResponseModel> PutAuthenticator(
[FromBody] TwoFactorAuthenticatorUpdateRequestModel model)
{
var user = model.ToUser(await _userService.GetUserByPrincipalAsync(User));
var tokenIsValid =
_twoFactorAuthenticatorDataProtector.TryUnprotect(model.UserVerificationToken, out var decryptedToken)
&& decryptedToken.Valid
&& decryptedToken.TokenIsValid(user, model.Key);
if (!tokenIsValid)
{
throw new BadRequestException("UserVerificationToken", "User verification failed.");
}
if (!await _userManager.VerifyTwoFactorTokenAsync(user,
CoreHelpers.CustomProviderName(TwoFactorProviderType.Authenticator), model.Token))
{
throw new BadRequestException("Token", "Invalid token.");
}
await _userService.UpdateTwoFactorProviderAsync(user, TwoFactorProviderType.Authenticator);
return new TwoFactorAuthenticatorUpdateResponseModel(user);
}
[HttpPost("authenticator")]
[Obsolete("This endpoint is deprecated. Use PUT /authenticator instead.")]
public async Task<TwoFactorAuthenticatorUpdateResponseModel> PostAuthenticator(
[FromBody] TwoFactorAuthenticatorUpdateRequestModel model)
{
return await PutAuthenticator(model);
}
[HttpDelete("authenticator")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public async Task<IActionResult> DeleteAuthenticator(
[FromBody] TwoFactorAuthenticatorDeleteRequestModel model)View on GitHub (pinned to e93b962371)
Solutions
- Have the user generate and submit a fresh code from the authenticator app immediately (within the 30s window).
- Check for clock drift on the client device or server; TOTP allows a small window but large skew fails.
- Re-register the authenticator (get-authenticator flow) if the secret may be out of sync.
- Strip whitespace/formatting from the token before submission.
Example fix
// before
api.put('/users/two-factor/authenticator', { userVerificationToken, token: '12 34 56', key })
// after: sanitize and submit current code
const token = code.replace(/\s+/g, '');
api.put('/users/two-factor/authenticator', { userVerificationToken, token, key }); Defensive patterns
Strategy: validation
Validate before calling
const token = String(model.token).replace(/\D/g, '');
if (token.length !== 6) throw new Error('TOTP must be 6 digits'); Type guard
function isValidTotp(t): t is string { return /^\d{6}$/.test(String(t)); } Try / catch
try { await api.put('/users/two-factor/authenticator', model); }
catch (e) {
if (e.response?.data?.error?.errors?.Token) { throw new UserFacingError('The code is wrong or expired; generate a new one.'); }
throw e;
} Prevention
- Strip non-digits from TOTP before submitting.
- Submit within the 30s window; prompt the user to wait for a fresh code.
- Re-register the authenticator secret if codes consistently fail.
When it happens
Trigger: PUT /api/users/two-factor/authenticator (TwoFactorController line 149) where model.Token is wrong, expired (outside the TOTP time window), or computed from a different secret than the one registered for the user.
Common situations: Clock skew between the authenticator app and server, the user scanned the wrong QR / registered the secret on a second device, the code was entered after it rotated, or the secret was reset since minting the verification token.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- User verification failed.
- Invalid token.
- InvalidSsoToken
- SsoOrganizationIdMismatch
- UserIdAndTokenMismatch
AI-assisted analysis of bitwarden/server@e93b962371 (2026-08-13).
Data as JSON: /api/errors/8803510ad5f75a3c.
Report an issue: GitHub.