13 caveats · newest first
Passkeys' Caveats
Particular questions about usage, support, and edge-cases.
0x0cPasskeys’s & DomainsLATEST — Passkeys are unique to domains (Relying Party ID)
Due to the Webauthn specification, Passkeys are created against a particular website (e.g.,
Open 0x0c →A.xyz) as its “Relying Party ID”. This Passkey will only be able to be prompted during the visit from a browser to this domain. If this domain is no longer available, the Passkey will remain within the device of the user, but it will no longer be able to be prompted by any other website (e.g., B.xyz). Additionally, since only A.xyz was ever able to get the related public key of said Passkey as part of its creation, if the public key of the Passkey was not stored elsewhere, this public key is lost forever, and signatures can not be verified against it.This architecture poses a critical flaw into applications that do not use the Passkey in the intended Webauthn workflow, such as decentralized applications that only use the Passkey as an authentication mechanism for a smart wallet within the context of web3 (e.g., EIP 4337). However, as a work around, as long as the source code of A.xyz is available, the user can run it locally and trick a device where the Passkey is stored into loading A.xyz in its device instead of via the actual DNS call the domain resolved. This can be easily done in computer devices by updating the local /etc/hosts and using an SSL proxy with a self-signed certificate (e.g., via mkcert), but might pose a harder challenge in a mobile device.0x0bPasskeys’ Public Keys — Public key is only available during generation
Although part of the Passkey information is provided as part of the "signing" or "attestation" process of the
To access a Passkey public key, you need to
Open 0x0b →Webauthn workflow, the actual public key is NOT included in the response. The attestation includes a signature over the clientDataJSON and the authenticatorData, the latter including some information about the actual device used during the verification process. However, the public key data is only available during the registration part of the Webauthn workflow (i.e., the navigator.credentials.create call) and is not available to that particular Passkey anymore, not even during the navigator.credentials.get call.To access a Passkey public key, you need to
await for the response payload, and call the method response.getPublicKey(). Within TypeScript, you can cast the response of the credential as AuthenticatorAttestationResponse to have visibility of the getPublicKey method.0x0aiCloud backups — Passkeys in iCloud are by default backed up.
As a requirement to work with Passkeys in the Apple ecosystem, iCloud for Keychain needs to be enabled. As a result, Passkeys generated in an iOS device will be synced using Apple’s encrypted
Open 0x0a →HSM setup that protects all user’s iCloud accounts. This means that other iOS devices sharing the same Apple ID will automatically sync this Passkey. In case of 10 failed Apple ID attempts to recover an iCloud account, as detailed by Apple’s Terms of Service, the account will be locked, and no further information, included Passkeys connected to this account, can be recovered.Passkeys can be backed up to a different Apple ID account using iOS’s Airdrop feature. Both users (sender and recipient) need to be in each other contacts’s list. After sending the Passkey, it will be available on the recipient’s phone via its traditional biometrics workflow. Although an iOS device can send a Passkey to a macOS device (e.g., macbook Air, macbook Pro), the latter can not make use of the Passkey, nor it is available via Keychain or Safari to log in to the website the Passkey was created in.0x09Authenticators setup — Requests can force specific authenticators.
The property
There are a couple of edge-cases where a platform authenticator can be toggled on and off (e.g., a keyboard with a fingerprint reader with FIDO2 support) and
Open 0x09 →authenticatorSelection and its children authenticatorAttachment can determine a preference for a particular authenticator. If selected cross-platform, then the webauthn authentication catalog will only show support for roaming authenticators (e.g., Yubikey). At the same time, if selected platform, it will request only biometrics-supported authentication. By default (i.e., if the property is missing), no preference is given and thus, both options can be selected.There are a couple of edge-cases where a platform authenticator can be toggled on and off (e.g., a keyboard with a fingerprint reader with FIDO2 support) and
platform is preferred. If the only authenticator is this device, then the workflow will continue w/o even prompting signature using the previously loaded key before the authenticator was disconnected.0x08Public key in DER format — Webauthn exports the public key in DER format.
The easiest way to retrieve the public key of a user during the webauthn workflow is by calling
Finally, bear in mind that when using this method, the public key is returned in DER format, and not in CBOR format as it’s being retrieved from the
Open 0x08 →getPublicKey() of the response object returned during the credential creation process. To ensure you have access to this object, cast the response with the type AuthenticatorAttestationResponse, otherwise the method won’t be available. Bear in mind this interface is only available during creation of the Passkey and not during retrieval (e.g. get call) of the credential, where an assertation against the server data is being created.Finally, bear in mind that when using this method, the public key is returned in DER format, and not in CBOR format as it’s being retrieved from the
authenticatorData payload. You can import and manipulate this key (originally an ArrayBuffer) as a CryptoKey using the Web Cryptographi API crypto.subtle.importKey method passing the spki format as parameter.0x07No p-384 keys support — Passkeys don’t support other keys other than p-256.
Despite documentation describing support for
Open 0x07 →p-384 curves by using -35 as a pubKeyCredParams instead of the standard -7 as part of the PublicKeyCredentialCreationOptions object, no platform ID seems to be fully supporting this curve.0x06Client side key creation — Passkeys can be created from the dev tools pane.
If you provide a correct
Open 0x06 →navigator.credentials.create code via the Developers Toolbar, you can trigger the webauthn workflow. The key will be then created in the client's Passkey storage. If this action was executed in localhost and there was a key already in that domain, it will be replaced. However, in any other domain, even if the rp property matches, it will not overwrite the existing one but create a new one instead.0x05Create workflow can err — Passkeys calls are error-prone, based on user input.
Despite what would be correct code, the
Open 0x05 →navigator.credentials.create code might throw an error with a Request cancelled by user independently on whether the user accepted or rejected the request.0x04Length of rawId and id — Passkeys’s properties rawId and id aren’t consistent.
Passkeys have a
Open 0x04 →id property equal to their rawId value (an ArrayBuffer) encoded in base64. However, the id property will remove the padding of the value (i.e. the = character), making it sometimes a different length than if you were to encode the rawId in bas64 yourself. Both values are valid base64 encoded strings (i.e., decoders will understand them) but ideally default to using id to avoid these length discrepancies.0x03Available Passkeys — It is not possible to see what Passkeys a user has.
Passkeys relies on
Open 0x03 →navigator.credentials.get to obtain a specific Passkey to load in the application. However, there’s no way for the website trying to authenticate your Passkey to access the ones you currently have available.0x02Username vs Display Name — It is unclear which property is displayed.
Most authenticators will use the
Open 0x02 →name property when prompting interacting with the navigator.credentials interface. However, navigator.credentials.create takes both name (usually an email or username) and displayName as variables. It is not very evident what are the differences between both and (or when, actually) to use or change which one.0x01Rejected creation — Passkeys can always be rejected.
Although an application can prompt the creation of a Passkey, it's ultimately up to the user to accept its creation. A rejection to do so will throw an 'NotAllowedError' Error, which needs to be escaped to avoid bubbling up the exception.
Open 0x01 →0x00Secure context — Passkeys will not work in localhost.
As with many cryptographic related applications, Passkeys will not be available in insecure contexts. This means that Passkeys will not be available in
Open 0x00 →HTTP contexts, but will be available in HTTPS contexts. This includes localhost, which is not considered a secure context.