bitwarden/server · error · BadRequestException
WebAuthn prf keys must have user-key during rotation.
Error message
WebAuthn prf keys must have user-key during rotation.
What it means
Thrown by WebAuthnLoginKeyRotationValidator when a rotation entry matched a PRF-enabled credential (matched by Id) but its EncryptedUserKey is null. PRF credentials store an encrypted copy of the user key, so a matched entry must supply the re-encrypted user key; a null value leaves the credential unusable and is rejected.
Source
Thrown at src/Api/KeyManagement/Validators/WebAuthnLoginKeyRotationValidator.cs:46
var result = new List<WebAuthnLoginRotateKeyData>();
var validCredentials = (await _webAuthnCredentialRepository.GetManyByUserIdAsync(user.Id))
.Where(credential => credential.GetPrfStatus() == WebAuthnPrfStatus.Enabled).ToList();
if (validCredentials.Count == 0)
{
return result;
}
foreach (var webAuthnCredential in validCredentials)
{
var keyToRotate = keysToRotate.FirstOrDefault(c => c.Id == webAuthnCredential.Id);
if (keyToRotate == null)
{
throw new BadRequestException("All existing webauthn prf keys must be included in the rotation.");
}
if (keyToRotate.EncryptedUserKey == null)
{
throw new BadRequestException("WebAuthn prf keys must have user-key during rotation.");
}
if (keyToRotate.EncryptedPublicKey == null)
{
throw new BadRequestException("WebAuthn prf keys must have public-key during rotation.");
}
result.Add(keyToRotate.ToWebAuthnRotateKeyData());
}
return result;
}
}
View on GitHub (pinned to e93b962371)
Solutions
- Re-encrypt the user key for every PRF-enabled credential and populate EncryptedUserKey.
- Add a pre-send assertion that every key entry has a non-null EncryptedUserKey.
- If a credential cannot be re-encrypted, disable its PRF or remove the passkey before rotating.
- Log the failing credential Id to identify the under-populated entry.
Example fix
// before
{ id: c.id, encryptedPublicKey: reencrypt(c.publicKey) }
// after
{ id: c.id, encryptedUserKey: reencrypt(c.userKey), encryptedPublicKey: reencrypt(c.publicKey) } Defensive patterns
Strategy: validation
Validate before calling
const missingUserKey = payload.keys.filter(k => k.encryptedUserKey == null);
if (missingUserKey.length) {
throw new Error(`WebAuthn keys missing user key: ${missingUserKey.map(k => k.id).join(', ')}`);
} Type guard
function hasWebAuthnUserKey(k: { encryptedUserKey?: string | null }): boolean {
return k.encryptedUserKey != null && k.encryptedUserKey.length > 0;
} Try / catch
try {
await api.rotateKey(payload);
} catch (e) {
if (e.status === 400 && /must have user-key/i.test(e.message)) {
payload.keys = payload.keys.map(k => ({ ...k, encryptedUserKey: k.encryptedUserKey ?? reencrypt(userKeyFor(k.id)) }));
return api.rotateKey(payload);
}
throw e;
} Prevention
- Assert every PRF key entry has a non-null EncryptedUserKey before submitting.
- Disable PRF on a credential if its user key cannot be re-encrypted.
- Centralize the re-encryption mapper so the user-key field is always populated.
When it happens
Trigger: The keys array includes the credential Id but EncryptedUserKey is null or was omitted during request construction.
Common situations: Client reused a template and forgot to set EncryptedUserKey for a newly-added PRF credential; serialization dropped the field; the re-encryption of the user key for that credential failed silently.
Related errors
- WebAuthn prf keys must have public-key during rotation.
- All existing webauthn prf keys must be included in the rotat
- Rotated encryption keys must be provided for all devices tha
- Emergency access keys cannot be set to null during rotation.
- Reset Password keys cannot be set to null during rotation.
AI-assisted analysis of bitwarden/server@e93b962371 (2026-08-13).
Data as JSON: /api/errors/bc03963c82f25936.
Report an issue: GitHub.