bitwarden/server · error · BadRequestException

Invalid password.

Error message

Invalid password.

What it means

Thrown as a 400 BadRequestException("MasterPasswordHash", "Invalid password.") from the organization API-key retrieval endpoint. After resolving the user and the organization API key, the caller's secret (model.Secret) is verified via _userService.VerifySecretAsync; on failure a 2-second delay runs and the error is keyed under MasterPasswordHash. This guards retrieval of an organization API key (e.g., SCIM key) behind master-password re-verification.

Source

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

            {
                throw new NotFoundException();
            }
        }

        var organizationApiKey = await _getOrganizationApiKeyQuery
                                     .GetOrganizationApiKeyAsync(organization.Id, model.Type) ??
                                 await _createOrganizationApiKeyCommand.CreateAsync(organization.Id, model.Type);

        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("MasterPasswordHash", "Invalid password.");
        }

        var response = new ApiKeyResponseModel(organizationApiKey);
        return response;
    }

    [HttpGet("{id}/api-key-information/{type?}")]
    public async Task<ListResponseModel<OrganizationApiKeyInformation>> ApiKeyInformation(Guid id,
        [FromRoute] OrganizationApiKeyType? type)
    {
        if (!await HasApiKeyAccessAsync(id, type))
        {
            throw new NotFoundException();
        }

        var apiKeys = await _organizationApiKeyRepository.GetManyByOrganizationIdTypeAsync(id, type);

        return new ListResponseModel<OrganizationApiKeyInformation>(

View on GitHub (pinned to e93b962371)

Solutions

  1. Re-derive MasterPasswordHash with the user's current KDF configuration and resubmit model.Secret.
  2. If the password was recently changed, sign out and back in so the client recomputes the hash.
  3. Confirm the field sent is the secret the endpoint expects (Secret) and that it is the derived hash, not plaintext.
  4. Verify the authenticated principal is the account whose secret is being checked.

Example fix

// before
body.Secret = oldHash;
// after
body.Secret = await crypto.hashPassword(masterPassword, user.kdf);
await api.post(`organizations/${id}/api-key`, body);
Defensive patterns

Strategy: try-catch

Validate before calling

function validSecret(secret) {
  return typeof secret === 'string' && 0 < secret.length && secret.length < 1024;
}

Type guard

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

Try / catch

try {
  await api.post(`organizations/${id}/api-key`, body);
} catch (e) {
  if (e?.response?.status === 400 && e.response.data?.ValidationErrors?.['MasterPasswordHash']?.some(m => /Invalid password/i.test(m))) {
    promptForMasterPasswordAgain();
  } else throw e;
}

Prevention

When it happens

Trigger: POST to get/create an organization API key with a valid session and existing API key record, but model.Secret does not verify against the current user. Occurs when the admin's stored master-password hash differs from the supplied hash.

Common situations: Admin entered the wrong master password in the 'verify to view API key' prompt; client derived the hash with outdated KDF iterations after a server-side KDF change; password was changed elsewhere and the local session still holds the old derived hash.

Related errors


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