c2sp.org/passkey-record
This is the development version of this specification, rendered from the tip of the main branch.

This document specifies an encoding for a WebAuthn credential record, or passkey record, for use by Relying Parties (server applications and WebAuthn server libraries) as an interoperable storage format, and as an opaque input to verification.

It reuses the grammar of Password Hashing Competition (PHC) Strings and the binary encoding of the WebAuthn authenticator data.

$webauthn$v=1$transports=hybrid+internal$<base64 authenticator data>

Introduction §

The WebAuthn specification defines an abstract credential record, but no concrete representation. The encoding specified in this document is intended to be an interoperable format for credential record storage, analogously to password hash strings.

Like a password hash, a passkey record is immutable. (Mutable values include essentially only the sign count and the backup state. The former is mostly unused in large deployments. The latter can be extracted from the authentication response upon log in.)

The record is sufficient for verification. The set of records associated with an account can be the input of a login function, along with the request challenge and the authenticator response.

Format §

In the PHC String syntax, a WebAuthn credential record has

(According to c2sp.org/phc-strings, if there is only one of salt and payload, it is considered a salt.)

Prefix §

A record MUST start with

$webauthn$v=1$

Transports parameters §

If available, the credential’s transports list MAY be encoded by joining each transport name with a + character, and then appending to the prefix the string transports=, followed by the encoded list and a $ character. The list MUST be sorted lexicographically and deduplicated.

If any of the transports is empty or contains non-ASCII characters or characters besides lowercase letters, uppercase letters, digits, /, ., and -, the parameter MUST be omitted. If the list is empty, the parameter MUST be omitted.

Implementations SHOULD bound the number and length of transports when producing or parsing a record. 32 transports of 32 characters each is a reasonable limit.

Parsers MUST reject a record with more than one transports parameter, or whose transports value is empty, contains an empty transport, contains a transport with characters outside the set above, or is not sorted and deduplicated.

The transports are returned by the getTransports() method of the AuthenticatorAttestationResponse interface, and are encoded as the .response.transports array in the JSON encoding of an AuthenticatorAttestationResponse returned by navigator.credentials.create().

Parsers MUST ignore other parameters, but MUST reject a record with a parameter that does not match the PHC String grammar: a name of one to 32 characters from [a-z0-9-] other than v, and a value, possibly empty, from [a-zA-Z0-9/+.-].

Payload §

Next, the authenticator data is appended encoded with standard Base64 without padding = signs or whitespace. The authenticator data MUST have the AT flag set, indicating it was produced during registration.

Implementations SHOULD reject a record whose authenticator data exceeds 8192 bytes (enough for a maximum-length credential ID and a large post-quantum public key).

The authenticator data is a binary structure defined in the WebAuthn Level 3 specification, is returned by the getAuthenticatorData() method of the AuthenticatorAttestationResponse, and is the .response.authenticatorData value in the JSON encoding of an AuthenticatorAttestationResponse returned by navigator.credentials.create(). Note that the JSON value is encoded with the base64url alphabet, while the record must be encoded with the standard base64 alphabet.

The authenticator data includes the Credential ID, the public key, the UV, BE, and BS flags (as of registration), the AAGUID, and the hash of the Relying Party ID.

Records SHOULD be stored keyed by user ID only, and SHOULD NOT be indexed by Credential ID. If there is no Credential ID index, Credential ID uniqueness SHOULD NOT be enforced.

(Uniqueness is only necessary to avoid cross-account ambiguity, which is not an issue if records are exclusively looked up by user ID.)

User IDs SHOULD be random identifiers with at least 120 bits of entropy.

Multiple records SHOULD be allowed per user ID.

WebAuthn doesn’t require storing anything else than user ID and passkey record(s). Applications MAY store additional fields for the passkey management UI, such as a nickname, and creation and last use timestamps.

Test vectors [TODO] §