bitwarden/server · error · BadRequestException

User verification failed.

Error message

User verification failed.

What it means

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'.

Source

Thrown at src/Api/AdminConsole/Controllers/OrganizationsController.cs:312

            throw new NotFoundException();
        }

        var organization = await _organizationRepository.GetByIdAsync(orgIdGuid);
        if (organization == null)
        {
            throw new NotFoundException();
        }

        var user = await _userService.GetUserByPrincipalAsync(User);
        if (user == null)
        {
            throw new UnauthorizedAccessException();
        }

        if (!await _userService.VerifySecretAsync(user, model.Secret))
        {
            await Task.Delay(2000);
            throw new BadRequestException(string.Empty, "User verification failed.");
        }

        if (organization.IsValidClient())
        {
            var provider = await _providerRepository.GetByOrganizationIdAsync(organization.Id);

            if (provider.IsBillable())
            {
                await _providerBillingService.ScaleSeats(
                    provider,
                    organization.PlanType,
                    -organization.Seats ?? 0);
            }
        }

        await _organizationDeleteCommand.DeleteAsync(organization);
    }

View on GitHub (pinned to e93b962371)

Solutions

  1. Re-prompt the admin for their master password and re-derive MasterPasswordHash with the current KDF settings before resubmitting.
  2. Verify the request is being made by the same authenticated principal whose secret is being checked (no session/user mismatch).
  3. 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.
  4. If the user recently changed their password or KDF, force a client relock/re-auth so the derived hash matches the server verifier.

Example fix

// before: sending raw password as the secret
body.Secret = rawPassword;
// after: derive the master-password hash client-side first
body.Secret = await crypto.hashPassword(rawPassword, user.kdf);
await api.post(`organizations/${id}/delete-recover`, body);
Defensive patterns

Strategy: try-catch

Validate before calling

// Cannot know server-side correctness ahead of time, but validate shape
function validSecretPayload(secret) {
  return typeof secret === 'string' && secret.length > 0 && secret.length < 1024;
}

Type guard

function isSecretVerificationModel(v): v is { secret: string } {
  return !!v && typeof v.secret === 'string' && v.secret.length > 0;
}

Try / catch

try {
  await api.post(`organizations/${id}/delete-recover`, body);
} catch (e) {
  if (e?.response?.status === 400 && /User verification failed/i.test(e.response.data?.ValidationErrors?.['']?.[0] ?? '')) {
    promptForMasterPasswordAgain();
  } else throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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).

Related errors


AI-assisted analysis of bitwarden/server@e93b962371 (2026-08-13). Data as JSON: /api/errors/b81e0311918a04c5. Report an issue: GitHub.