Origin
Native Application (non-browser app)
Web URL or App name
Any relying party that sends spec-compliant creation options. This is in the JSON parsing, so it is not specific to one site or app.
Passkey Action
Build Information
Password Manager 2026.8.0 (21819) on device. Also reproduced against main at b99ad92 with the unit test runner.
Additional Information
PasskeyAttestationOptions does not match PublicKeyCredentialCreationOptionsJSON from the WebAuthn spec in four places. One of them breaks registration outright, the other three drop data without any error.
1. authenticatorSelection is treated as required.
In the spec only rp, user, challenge and pubKeyCredParams are required. authenticatorSelection is optional. In PasskeyAttestationOptions.kt it is a non-nullable property with no default, so a request that leaves it out fails to deserialize:
kotlinx.serialization.MissingFieldException: Field 'authenticatorSelection' is required
for type with serial name 'PasskeyAttestationOptions', but it was missing at path: $
explicitNulls = false does not help here because it only relaxes nullable properties, and coerceInputValues = true only rewrites values that are present but invalid.
Once the parse fails, getPasskeyAttestationOptionsOrNull returns null and registration ends in Fido2RegisterCredentialResult.Error.MissingHostUrl for an unprivileged caller, or Error.InternalError for a privileged one. The add item screen also falls back to the package name and an empty username.
2. excludeCredentials is spelled excludedCredentials.
@SerialName("excludedCredentials") never matches, so the exclusion list from the relying party is silently replaced with an empty list. ignoreUnknownKeys = true is what hides it.
This is not only a parsing detail. registerFido2CredentialInternal re-serializes the parsed model and sends that JSON to the SDK, so the exclusion list never reaches the authenticator and a duplicate passkey is not prevented.
Worth noting that the fixtures in this repo already use the spec name with an empty array, which is exactly why the tests never caught it:
RelyingPartyParserTest.kt:105 and :139
BitwardenCredentialManagerTest.kt:1520
3. AuthenticatorAttachment.CROSS_PLATFORM is mapped to cross_platform.
The spec value has a hyphen. With coerceInputValues = true the unknown member is coerced to the property default, so "authenticatorAttachment": "cross-platform" decodes to null and then disappears from the JSON handed to the SDK.
4. ResidentKeyRequirement is missing discouraged.
The spec enum is discouraged, preferred, required. Only the last two are declared, so "residentKey": "discouraged" is coerced to null and dropped the same way.
I have a patch with unit tests ready and will open a PR against this issue.
Origin
Native Application (non-browser app)
Web URL or App name
Any relying party that sends spec-compliant creation options. This is in the JSON parsing, so it is not specific to one site or app.
Passkey Action
Build Information
Password Manager 2026.8.0 (21819) on device. Also reproduced against
mainat b99ad92 with the unit test runner.Additional Information
PasskeyAttestationOptionsdoes not matchPublicKeyCredentialCreationOptionsJSONfrom the WebAuthn spec in four places. One of them breaks registration outright, the other three drop data without any error.1.
authenticatorSelectionis treated as required.In the spec only
rp,user,challengeandpubKeyCredParamsare required.authenticatorSelectionis optional. InPasskeyAttestationOptions.ktit is a non-nullable property with no default, so a request that leaves it out fails to deserialize:explicitNulls = falsedoes not help here because it only relaxes nullable properties, andcoerceInputValues = trueonly rewrites values that are present but invalid.Once the parse fails,
getPasskeyAttestationOptionsOrNullreturns null and registration ends inFido2RegisterCredentialResult.Error.MissingHostUrlfor an unprivileged caller, orError.InternalErrorfor a privileged one. The add item screen also falls back to the package name and an empty username.2.
excludeCredentialsis spelledexcludedCredentials.@SerialName("excludedCredentials")never matches, so the exclusion list from the relying party is silently replaced with an empty list.ignoreUnknownKeys = trueis what hides it.This is not only a parsing detail.
registerFido2CredentialInternalre-serializes the parsed model and sends that JSON to the SDK, so the exclusion list never reaches the authenticator and a duplicate passkey is not prevented.Worth noting that the fixtures in this repo already use the spec name with an empty array, which is exactly why the tests never caught it:
RelyingPartyParserTest.kt:105and:139BitwardenCredentialManagerTest.kt:15203.
AuthenticatorAttachment.CROSS_PLATFORMis mapped tocross_platform.The spec value has a hyphen. With
coerceInputValues = truethe unknown member is coerced to the property default, so"authenticatorAttachment": "cross-platform"decodes to null and then disappears from the JSON handed to the SDK.4.
ResidentKeyRequirementis missingdiscouraged.The spec enum is
discouraged,preferred,required. Only the last two are declared, so"residentKey": "discouraged"is coerced to null and dropped the same way.I have a patch with unit tests ready and will open a PR against this issue.