{"record":{"id":"7aa5cd417c0d1e5d","repo":"bitwarden/server","slug":"invalid-password-7aa5cd","errorCode":null,"errorMessage":"Invalid password.","messagePattern":"Invalid password\\.","errorType":"validation","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"src/Api/Auth/Controllers/AccountsController.cs","lineNumber":140,"sourceCode":"        // TODO: PM-39120 - PM30806_SelfServiceChangeEmailCommand flag cleanup, remove the flag\n        // check and keep only the SelfServiceChangeEmailCommand call.\n        if (_featureService.IsEnabled(FeatureFlagKeys.PM30806_SelfServiceChangeEmailCommand))\n        {\n            await _selfServiceChangeEmailCommand.InitiateChangeEmailAsync(\n                user, model.MasterPasswordHash, model.NewEmail);\n\n            return;\n        }\n\n        if (user.UsesKeyConnector)\n        {\n            throw new BadRequestException(\"You cannot change your email when using Key Connector.\");\n        }\n\n        if (!await _userService.CheckPasswordAsync(user, model.MasterPasswordHash))\n        {\n            await Task.Delay(2000);\n            throw new BadRequestException(\"MasterPasswordHash\", \"Invalid password.\");\n        }\n\n        var claimedUserValidationResult = await _userService.ValidateClaimedUserDomainAsync(user, model.NewEmail);\n\n        if (!claimedUserValidationResult.Succeeded)\n        {\n            throw new BadRequestException(claimedUserValidationResult.Errors);\n        }\n\n        await _userService.InitiateEmailChangeAsync(user, model.NewEmail);\n    }\n\n    [HttpPost(\"email\")]\n    public async Task PostEmail([FromBody] EmailRequestModel model)\n    {\n        var user = await _userService.GetUserByPrincipalAsync(User);\n        if (user == null)\n        {","sourceCodeStart":122,"sourceCodeEnd":158,"githubUrl":"https://github.com/bitwarden/server/blob/e93b962371d80964556f5590c6615f5160a437a1/src/Api/Auth/Controllers/AccountsController.cs#L122-L158","documentation":"Thrown as a 400 BadRequestException(\"MasterPasswordHash\", \"Invalid password.\") from the legacy path of POST accounts/email-token when _userService.CheckPasswordAsync(user, model.MasterPasswordHash) returns false. The supplied master-password hash does not match the user's current verifier; a 2-second delay precedes the throw to mitigate timing attacks.","triggerScenarios":"An authenticated, non-Key-Connector user (flag OFF path) calls POST accounts/email-token with a master-password hash that fails verification. The user exists and is not a Key Connector user, but the hash is wrong.","commonSituations":"User mistyped the master password; client derived the hash with outdated KDF settings after a server-side KDF rotation; password was changed elsewhere and the local session holds a stale derived hash.","solutions":["Re-derive MasterPasswordHash using the user's current KDF configuration and resubmit.","Re-authenticate if the master password was changed since the session started.","Confirm the hash sent is the derived hash (not plaintext) and maps to the authenticated principal.","Prompt the user to re-enter the master password and retry."],"exampleFix":"// before\nbody.masterPasswordHash = staleHash;\n// after\nbody.masterPasswordHash = await crypto.hashPassword(masterPassword, user.kdf);\nawait api.post('accounts/email-token', body);","handlingStrategy":"try-catch","validationCode":"function validHash(h) { return typeof h === 'string' && h.length > 0 && h.length < 1024; }","typeGuard":"function isEmailTokenModel(v): v is { masterPasswordHash: string; newEmail: string } {\n  return !!v && typeof v.masterPasswordHash === 'string' && typeof v.newEmail === 'string';\n}","tryCatchPattern":"try { await api.post('accounts/email-token', body); }\ncatch (e) {\n  if (e?.response?.status === 400 && e.response.data?.ValidationErrors?.['MasterPasswordHash']?.some(m => /Invalid password/i.test(m))) promptForMasterPasswordAgain();\n  else throw e;\n}","preventionTips":["Derive MasterPasswordHash with the current KDF before submitting.","Re-authenticate after a password or KDF change.","Re-prompt for the master password before sensitive account changes."],"tags":["bitwarden","authentication","secret-verification","email-change","aspnet"],"backgroundTag":null,"analyzedSha":"e93b962371d80964556f5590c6615f5160a437a1","analyzedAt":"2026-08-13T14:22:19.382Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}