{"record":{"id":"8307e3ef74e4aeb9","repo":"bitwarden/server","slug":"name-is-invalid","errorCode":null,"errorMessage":"{name} is invalid.","messagePattern":"(.+?) is invalid\\.","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"src/Api/Auth/Controllers/TwoFactorController.cs","lineNumber":585,"sourceCode":"    /// <summary>Mints a protected user-verification token bound to <paramref name=\"user\"/> and <paramref name=\"providerType\"/>.</summary>\n    private string MintProtectedUserVerificationToken(User user, TwoFactorProviderType providerType)\n    {\n        var token = _twoFactorUserVerificationTokenableFactory.CreateToken(user, providerType);\n        return _twoFactorUserVerificationDataProtector.Protect(token);\n    }\n\n    private async Task ValidateYubiKeyAsync(User user, string name, string value)\n    {\n        if (string.IsNullOrWhiteSpace(value) || value.Length == 12)\n        {\n            return;\n        }\n\n        if (!await _userManager.VerifyTwoFactorTokenAsync(user,\n                CoreHelpers.CustomProviderName(TwoFactorProviderType.YubiKey), value))\n        {\n            await Task.Delay(2000);\n            throw new BadRequestException(name, $\"{name} is invalid.\");\n        }\n\n        await Task.Delay(500);\n    }\n\n    private bool ValidateSsoEmail2FaToken(string ssoEmail2FaSessionToken, User user)\n    {\n        return _ssoEmailTwoFactorSessionDataProtector.TryUnprotect(ssoEmail2FaSessionToken, out var decryptedToken) &&\n               decryptedToken.Valid && decryptedToken.TokenIsValid(user);\n    }\n\n    private async Task ThrowDelayedBadRequestExceptionAsync(string message, int delayTime = 2000)\n    {\n        await Task.Delay(delayTime);\n        throw new BadRequestException(message);\n    }\n}\n","sourceCodeStart":567,"sourceCodeEnd":603,"githubUrl":"https://github.com/bitwarden/server/blob/e93b962371d80964556f5590c6615f5160a437a1/src/Api/Auth/Controllers/TwoFactorController.cs#L567-L603","documentation":"BadRequestException with key = name is thrown in ValidateYubiKeyAsync when the supplied YubiKey value is non-empty, not length 12, and fails _userManager.VerifyTwoFactorTokenAsync for the YubiKey provider. A 2-second delay is intentionally applied before throwing to slow brute-force attempts; a 500ms delay applies on the success path for timing consistency.","triggerScenarios":"PUT /two-factor/yubikey or similar flow calling ValidateYubiKeyAsync where one of multiple YubiKey values (key1/key2/key3) is present but invalid (wrong OTP, partial input, or a non-YubiKey string).","commonSituations":"User touched the YubiKey only briefly (partial OTP), pasted a truncated string, the slot was configured for a different credential, or the OTP was already used (YubiKey OTPs are single-use server-side).","solutions":["Have the user fully touch the YubiKey to emit the complete 44-char OTP and resubmit immediately.","Reject empty or partial values client-side before sending (a valid OTP is 44 chars; a placeholder is 12).","Ensure the YubiKey slot is configured for the Bitwarden credential.","Generate a fresh OTP for each request (OTPs are one-time-use)."],"exampleFix":"// before\nvalidateYubiKey('ccccccbc') // partial OTP\n// after\nfunction isValidYubiKeyOtp(v) { return v.length === 44 || v.length === 12; }\nif (!isValidYubiKeyOtp(value)) throw new Error('Invalid YubiKey input');\nawait validateYubiKey(value);","handlingStrategy":"validation","validationCode":"function isValidYubiKeyInput(v) { const s = String(v ?? ''); return s.length === 0 || s.length === 12 || s.length === 44; }\nif (!isValidYubiKeyInput(value)) throw new Error('Invalid YubiKey input length');","typeGuard":"function isYubiKeyOtp(v): v is string { return typeof v === 'string' && v.length === 44; }","tryCatchPattern":"try { await api.put('/users/two-factor/yubikey', model); }\ncatch (e) {\n  if (e.response?.status === 400 && /invalid/i.test(JSON.stringify(e.response.data?.error))) {\n    throw new UserFacingError('YubiKey OTP invalid; touch the key again and resubmit.');\n  }\n  throw e;\n}","preventionTips":["Validate OTP length (44) or placeholder (12) before sending.","Use each OTP only once.","Expect a 2s server-side delay on failure; avoid tight retry loops."],"tags":["two-factor","yubikey","otp","rate-limiting"],"backgroundTag":null,"analyzedSha":"e93b962371d80964556f5590c6615f5160a437a1","analyzedAt":"2026-08-13T14:22:19.382Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}