{"record":{"id":"daf842a6308a83dc","repo":"bitwarden/server","slug":"unable-to-complete-webauthn-registration-daf842","errorCode":null,"errorMessage":"Unable to complete WebAuthn registration.","messagePattern":"Unable to complete WebAuthn registration\\.","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"src/Api/Auth/Controllers/WebAuthnController.cs","lineNumber":117,"sourceCode":"    }\n\n    [Authorize(Policies.Application)]\n    [HttpPost(\"\")]\n    public async Task<WebAuthnCredentialResponseModel> Post([FromBody] WebAuthnLoginCredentialCreateRequestModel model)\n    {\n        var user = await GetUserAsync();\n        await ValidateIfUserCanUsePasskeyLogin(user.Id);\n        var tokenable = _createOptionsDataProtector.Unprotect(model.Token);\n\n        if (!tokenable.TokenIsValid(user))\n        {\n            throw new BadRequestException(\"The token associated with your request is expired. A valid token is required to continue.\");\n        }\n\n        var credential = await _createWebAuthnLoginCredentialCommand.CreateWebAuthnLoginCredentialAsync(user, model.Name, tokenable.Options, model.DeviceResponse, model.SupportsPrf, model.EncryptedUserKey, model.EncryptedPublicKey, model.EncryptedPrivateKey);\n        if (credential == null)\n        {\n            throw new BadRequestException(\"Unable to complete WebAuthn registration.\");\n        }\n\n        return new WebAuthnCredentialResponseModel(credential);\n    }\n\n    private async Task ValidateIfUserCanUsePasskeyLogin(Guid userId)\n    {\n        var requireSsoPolicyRequirement = await _policyRequirementQuery.GetAsyncVNext<RequireSsoPolicyRequirement>(userId);\n\n        if (!requireSsoPolicyRequirement.CanUsePasskeyLogin)\n        {\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)","sourceCodeStart":99,"sourceCodeEnd":135,"githubUrl":"https://github.com/bitwarden/server/blob/e93b962371d80964556f5590c6615f5160a437a1/src/Api/Auth/Controllers/WebAuthnController.cs#L99-L135","documentation":"BadRequestException 'Unable to complete WebAuthn registration.' is thrown in POST /webauthn (WebAuthnController.Post) when _createWebAuthnLoginCredentialCommand.CreateWebAuthnLoginCredentialAsync returns null. The command performs fido2 attestation verification and persistence; null means attestation failed, the device response did not match the options, or the credential could not be saved.","triggerScenarios":"POST /webauthn (passkey creation, line 117) where model.DeviceResponse fails fido2 attestation/challenge verification against tokenable.Options, or the credential already exists (duplicate credential id).","commonSituations":"Device response generated against different options/origin/RP id, unsupported authenticator, expired challenge, duplicate registration of the same credential, or PRF/encrypted-key payload issues (model.EncryptedUserKey/EncryptedPublicKey/EncryptedPrivateKey malformed).","solutions":["Regenerate creation options and complete the ceremony in one flow without delay.","Verify the calling origin matches the server's fido2 RP id configuration.","Ensure EncryptedUserKey/EncryptedPublicKey/EncryptedPrivateKey are correctly encrypted for the user.","Avoid registering the same passkey twice; delete the old one first if needed."],"exampleFix":"// before\napi.post('/webauthn', { token, name, deviceResponse, supportsPrf: false, encryptedUserKey: null })\n// after\nconst encryptedUserKey = await cryptoService.encrypt(userKey);\napi.post('/webauthn', { token, name, deviceResponse, supportsPrf, encryptedUserKey, encryptedPublicKey, encryptedPrivateKey });","handlingStrategy":"validation","validationCode":"if (!model.token || !model.deviceResponse) throw new Error('Token and deviceResponse are required');\nif (model.supportsPrf && !model.encryptedUserKey) throw new Error('EncryptedUserKey required with PRF');","typeGuard":"function isWebAuthnCreateModel(m): m is WebAuthnCreateModel { return !!m?.token && !!m?.deviceResponse && typeof m.name === 'string'; }","tryCatchPattern":"try { await api.post('/webauthn', model); }\ncatch (e) {\n  if (e.response?.status === 400 && /WebAuthn registration/.test(e.response.data?.message)) {\n    throw new RetryableError('Restart the WebAuthn flow with fresh options');\n  }\n  throw e;\n}","preventionTips":["Match the origin/RP id to server fido2 config.","Provide all encrypted key fields when supportsPrf is true.","Avoid registering the same credential twice."],"tags":["webauthn","passkey","fido2","registration"],"backgroundTag":null,"analyzedSha":"e93b962371d80964556f5590c6615f5160a437a1","analyzedAt":"2026-08-13T14:22:19.382Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}