{"record":{"id":"b81e0311918a04c5","repo":"bitwarden/server","slug":"user-verification-failed","errorCode":null,"errorMessage":"User verification failed.","messagePattern":"User verification failed\\.","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"src/Api/AdminConsole/Controllers/OrganizationsController.cs","lineNumber":312,"sourceCode":"            throw new NotFoundException();\n        }\n\n        var organization = await _organizationRepository.GetByIdAsync(orgIdGuid);\n        if (organization == null)\n        {\n            throw new NotFoundException();\n        }\n\n        var user = await _userService.GetUserByPrincipalAsync(User);\n        if (user == null)\n        {\n            throw new UnauthorizedAccessException();\n        }\n\n        if (!await _userService.VerifySecretAsync(user, model.Secret))\n        {\n            await Task.Delay(2000);\n            throw new BadRequestException(string.Empty, \"User verification failed.\");\n        }\n\n        if (organization.IsValidClient())\n        {\n            var provider = await _providerRepository.GetByOrganizationIdAsync(organization.Id);\n\n            if (provider.IsBillable())\n            {\n                await _providerBillingService.ScaleSeats(\n                    provider,\n                    organization.PlanType,\n                    -organization.Seats ?? 0);\n            }\n        }\n\n        await _organizationDeleteCommand.DeleteAsync(organization);\n    }\n","sourceCodeStart":294,"sourceCodeEnd":330,"githubUrl":"https://github.com/bitwarden/server/blob/e93b962371d80964556f5590c6615f5160a437a1/src/Api/AdminConsole/Controllers/OrganizationsController.cs#L294-L330","documentation":"Thrown as a 400 BadRequestException (key \"\", message \"User verification failed.\") from the Admin Console organization delete-recover endpoint when _userService.VerifySecretAsync(user, model.Secret) returns false. The endpoint requires an authenticated admin to re-confirm their master password / client secret before a destructive org-delete-recover step. A deliberate 2-second Task.Delay precedes the throw to blunt timing attacks that would distinguish 'wrong secret' from 'valid secret'.","triggerScenarios":"POST to the organization delete-recover flow with a valid session (user resolved from principal) and an organization that exists, but model.Secret (master password hash or client secret) does not match the stored verifier. The org was found and the user was found, but secret verification failed.","commonSituations":"Admin typed the wrong master password; the client sent a stale or malformed MasterPasswordHash; the user's KDF/memory key was rotated but the client sent the old hash; secret sent for the wrong user account (session principal mismatch).","solutions":["Re-prompt the admin for their master password and re-derive MasterPasswordHash with the current KDF settings before resubmitting.","Verify the request is being made by the same authenticated principal whose secret is being checked (no session/user mismatch).","Check the client is sending model.Secret in the exact field the endpoint expects (Secret, not MasterPasswordHash) and that it is the PBKDF2/Argon2-derived hash, not the raw password.","If the user recently changed their password or KDF, force a client relock/re-auth so the derived hash matches the server verifier."],"exampleFix":"// before: sending raw password as the secret\nbody.Secret = rawPassword;\n// after: derive the master-password hash client-side first\nbody.Secret = await crypto.hashPassword(rawPassword, user.kdf);\nawait api.post(`organizations/${id}/delete-recover`, body);","handlingStrategy":"try-catch","validationCode":"// Cannot know server-side correctness ahead of time, but validate shape\nfunction validSecretPayload(secret) {\n  return typeof secret === 'string' && secret.length > 0 && secret.length < 1024;\n}","typeGuard":"function isSecretVerificationModel(v): v is { secret: string } {\n  return !!v && typeof v.secret === 'string' && v.secret.length > 0;\n}","tryCatchPattern":"try {\n  await api.post(`organizations/${id}/delete-recover`, body);\n} catch (e) {\n  if (e?.response?.status === 400 && /User verification failed/i.test(e.response.data?.ValidationErrors?.['']?.[0] ?? '')) {\n    promptForMasterPasswordAgain();\n  } else throw e;\n}","preventionTips":["Derive MasterPasswordHash client-side with the user's current KDF before sending.","Force a relock/re-auth after a KDF or password change so the derived hash matches.","Re-prompt for the master password immediately before destructive org operations."],"tags":["bitwarden","authentication","secret-verification","admin-console","aspnet"],"backgroundTag":null,"analyzedSha":"e93b962371d80964556f5590c6615f5160a437a1","analyzedAt":"2026-08-13T14:22:19.382Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}