{"record":{"id":"0f88e846d25ecf56","repo":"bitwarden/server","slug":"unable-to-update-credential","errorCode":null,"errorMessage":"Unable to update credential.","messagePattern":"Unable to update credential\\.","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"src/Api/Auth/Controllers/WebAuthnController.cs","lineNumber":146,"sourceCode":"        {\n            throw new BadRequestException(\"Passkeys cannot be created for your account. SSO login is required.\");\n        }\n    }\n\n    [Authorize(Policies.Application)]\n    [HttpPut()]\n    public async Task UpdateCredential([FromBody] WebAuthnLoginCredentialUpdateRequestModel model)\n    {\n        var tokenable = _assertionOptionsDataProtector.Unprotect(model.Token);\n        if (!tokenable.TokenIsValid(WebAuthnLoginAssertionOptionsScope.UpdateKeySet) || tokenable.Options == null)\n        {\n            throw new BadRequestException(\"The token associated with your request is invalid or has expired. A valid token is required to continue.\");\n        }\n\n        var (_, credential) = await _assertWebAuthnLoginCredentialCommand.AssertWebAuthnLoginCredential(tokenable.Options, model.DeviceResponse);\n        if (credential == null || credential.SupportsPrf != true)\n        {\n            throw new BadRequestException(\"Unable to update credential.\");\n        }\n\n        // assign new keys to credential\n        credential.EncryptedUserKey = model.EncryptedUserKey;\n        credential.EncryptedPrivateKey = model.EncryptedPrivateKey;\n        credential.EncryptedPublicKey = model.EncryptedPublicKey;\n\n        await _credentialRepository.UpdateAsync(credential);\n    }\n\n    [Authorize(Policies.Web)]\n    [HttpPost(\"{id}/delete\")]\n    public async Task Delete(Guid id, [FromBody] SecretVerificationRequestModel model)\n    {\n        var user = await VerifyUserAsync(model);\n        var credential = await _credentialRepository.GetByIdAsync(id, user.Id);\n        if (credential == null)\n        {","sourceCodeStart":128,"sourceCodeEnd":164,"githubUrl":"https://github.com/bitwarden/server/blob/e93b962371d80964556f5590c6615f5160a437a1/src/Api/Auth/Controllers/WebAuthnController.cs#L128-L164","documentation":"Thrown (HTTP 400) after a WebAuthn assertion succeeds but either no credential was resolved (credential == null) or the resolved authenticator does not report the PRF extension as supported (SupportsPrf != true). The key-rotation flow relies on PRF to derive the new user encryption keys, so a non-PRf passkey is ineligible to update the key set.","triggerScenarios":"User's passkey was created on an authenticator that does not expose the PRF extension (older security keys, some platform authenticators); the submitted deviceResponse signs over a credential id that matches no stored record; the credential was deleted between options-generation and assertion.","commonSituations":"Trying to rotate keys with a passkey enrolled on legacy hardware; browser/authenticator combination that does not surface PRF; mixed authenticator fleet where only some users have PRf-capable devices.","solutions":["Enroll a new passkey on a PRf-capable authenticator (recent platform authenticator + up-to-date browser) and retry key rotation.","Confirm the deviceResponse corresponds to a credential id still present for the user (refresh the credential list).","Fall back to a non-passkey key-rotation path for users without a PRf-capable authenticator."],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":"// Client-side: before attempting key rotation, confirm the chosen authenticator advertised PRF support during registration/assertion.","typeGuard":"// Server-side guard you can apply before reaching the throw site:\n// bool CanRotateViaPrf(WebAuthnCredential c) => c is not null && c.SupportsPrf == true;\n// Then: if (!CanRotateViaPrf(credential)) return GuidedFallback();","tryCatchPattern":"try {\n  await put('/webauthn', payload);\n} catch (e) {\n  if (e.isBadRequest && /unable to update credential/i.test(e.message)) {\n    await enrollPrfCapablePasskey(); // re-enroll on a PRf-capable device, then retry\n  } else { throw e; }\n}","preventionTips":["Surface the SupportsPrf flag in the credential list UI so users know which passkeys can rotate keys.","Enroll new passkeys on PRf-capable authenticators when key rotation may be needed.","Provide a non-passkey key-rotation fallback path."],"tags":["webauthn","passkey","prf","authentication","key-rotation"],"backgroundTag":null,"analyzedSha":"e93b962371d80964556f5590c6615f5160a437a1","analyzedAt":"2026-08-13T14:22:19.382Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}