WebinarLive on Nov 18: Building a Business Case for PasskeysPasskey Business Case, recording for all registrantsRegister
Back to Overview

What are PublicKeyCredentialCreationOptions in WebAuthn?

PublicKeyCredentialCreationOptions explained: all WebAuthn registration options incl. pubKeyCredParams, authenticatorSelection, hints and attestation.

Vincent Delitz
Vincent Delitz

Created: December 18, 2023

Updated: October 16, 2026

PublickeyCredentialRequestOptions play a crucial role in creating new credentials in WebAuthn by communicating required parameters to the client
Key Facts
  • PublicKeyCredentialCreationOptions is the WebAuthn dictionary generated by the server and passed to navigator.credentials.create() to register a new credential.
  • Only four members are required: rp, user, challenge and pubKeyCredParams. All other members are optional and fall back to browser defaults.
  • pubKeyCredParams should list EdDSA (-8), ES256 (-7) and RS256 (-257). Omitting RS256 breaks registration on Windows Hello TPM devices running Windows 10 or older Windows 11.
  • Setting residentKey to required and userVerification to required in authenticatorSelection creates discoverable credentials that work for usernameless login and Conditional UI.
  • Since WebAuthn Level 3, requesting attestation: none no longer zeroes the AAGUID, so you still see which passkey provider created the credential without cryptographic verification.

What are PublicKeyCredentialCreationOptions?#

PublicKeyCredentialCreationOptions is the WebAuthn dictionary that tells the browser how to create a new passkey or security key credential. The Relying Party server generates it, the frontend passes it to navigator.credentials.create({ publicKey }) and the authenticator answers with a new public key and an attestation.

Only four members are required: rp, user, challenge and pubKeyCredParams. Everything else is optional, but the optional members decide which authenticators can register, whether the passkey works for usernameless login and how much you learn about the device.

Example#

A typical registration request for a synced passkey, serialized as JSON (binary values are base64url encoded):

{ "rp": { "id": "passkeys.eu", "name": "Corbado Passkeys Demo" }, "user": { "id": "dXNyLZ...DU10Tc", "name": "john@doe.com", "displayName": "John Doe" }, "challenge": "888fix4Bus...pHHr3Y", "pubKeyCredParams": [ { "type": "public-key", "alg": -8 }, { "type": "public-key", "alg": -7 }, { "type": "public-key", "alg": -257 } ], "timeout": 300000, "excludeCredentials": [ { "type": "public-key", "id": "Xk2b...9fQ", "transports": ["internal", "hybrid"] } ], "authenticatorSelection": { "residentKey": "required", "requireResidentKey": true, "userVerification": "required" }, "hints": [], "attestation": "none", "extensions": { "credProps": true } }

Continue reading for a breakdown of every member and the values we recommend for different use cases.


Relevance in WebAuthn#

As shown in the registration flow, sending PublicKeyCredentialCreationOptions to the frontend is the first step of every WebAuthn registration. The backend stores the challenge, the browser forwards the options to the authenticator and the backend later verifies the response against exactly these options. Only generate creation options for a fully authenticated session (e.g. after MFA), otherwise an attacker with a stolen password can register their own passkey.

Substack Icon

Subscribe to our Passkeys Substack for the latest news.

Subscribe

All members at a glance#

MemberRequiredDefaultWhat it controls
rpYesrp.id = current domainWhich Relying Party the credential is bound to
userYesnoneWhich account the credential belongs to
challengeYesnoneRandom value that prevents replay
pubKeyCredParamsYesnoneAccepted signature algorithms, in order of preference
timeoutNobrowser specificHow long the browser waits for the user (300000 ms recommended)
excludeCredentialsNo[]Credentials that must not be registered again on the same device
authenticatorSelectionNosee belowAttachment, discoverability and user verification
hintsNo[]UI hints: security-key, client-device or hybrid
attestationNo"none"Whether and how the authenticator proves its make and model
attestationFormatsNo[]Preferred attestation statement formats (WebAuthn Level 3)
extensionsNo{}Additional features such as credProps, prf or credProtect

Technical breakdown#

Here is an explanation of every member as defined in the WebAuthn Level 3 specification.

rp#

"rp": { "id": "passkeys.eu", "name": "Corbado Passkeys Demo" }

rp identifies the Relying Party, the service the user registers with.

  • id: The RP ID is a registrable domain suffix of the current origin, e.g. passkeys.eu for app.passkeys.eu. If you leave it out, the browser uses the current domain. The credential can later only be used on this domain and its subdomains, so choose it carefully. It cannot be changed without re-registering all passkeys. To share passkeys across different domains, see Related Origin Requests.
  • name: A human-readable name for the service. It is still required, but deprecated in WebAuthn Level 3 because many clients do not display it. Setting it to the RP ID is a safe default. The former rp.icon and user.icon members were removed after Level 1.

user#

"user": { "id": "dXNyLZ...DU10Tc", "name": "john@doe.com", "displayName": "John Doe" }

user describes the account the credential is created for.

  • id: The user handle, an opaque byte sequence of 1 to 64 bytes chosen by the Relying Party. It must not contain personal data such as the email address or the customer number. Use a random value and store the mapping on the server. Registering again with the same user.id on the same authenticator replaces the existing passkey.
  • name: The identifier the user knows, e.g. the email address, username or customer number. Passkey managers show it in the account selector.
  • displayName: A friendly name such as "John Doe". It may be an empty string.

challenge#

"challenge": "888fix4Bus...pHHr3Y"

The cryptographic challenge is a random value of at least 16 bytes generated by the server for every ceremony. The authenticator signs it and the server verifies that the response contains the exact challenge it issued. Each challenge must be used only once and should expire with the timeout.

pubKeyCredParams#

"pubKeyCredParams": [ { "type": "public-key", "alg": -8 }, { "type": "public-key", "alg": -7 }, { "type": "public-key", "alg": -257 } ]

pubKeyCredParams lists the signature algorithms the server accepts for the new key pair, identified by their COSE algorithm identifier. The order expresses preference: the authenticator picks the first algorithm in the list that it supports. If it supports none of them, registration fails. If you send an empty list, browsers fall back to ES256 (-7) and RS256 (-257). Read more in our pubKeyCredParams deep dive.

COSE IDNameAlgorithmRecommendation
-8EdDSAEdDSA (Ed25519)Include, used by some security keys
-19Ed25519Ed25519 (fully specified, RFC 9864)Only in addition to -8, support is still rare
-7ES256ECDSA with P-256 and SHA-256Include, used by Apple, Google and most keys
-257RS256RSASSA-PKCS1-v1_5 with SHA-256Include, needed for Windows Hello on TPM devices
-35ES384ECDSA with P-384 and SHA-384Optional, test on old clients first
-36ES512ECDSA with P-521 and SHA-512Optional, test on old clients first
-37PS256RSASSA-PSS with SHA-256Optional, rarely used by authenticators
-65535RS1RSASSA-PKCS1-v1_5 with SHA-1Do not use

The WebAuthn specification recommends that Relying Parties include at least EdDSA (-8), ES256 (-7) and RS256 (-257) to support the widest range of authenticators. In practice almost every passkey is created with ES256. RS256 is the one most often forgotten and causes registration errors on Windows 10 and older Windows 11 devices whose Windows Hello keys live in the TPM.

Adding stronger algorithms such as ES384 or ES512 does not make your existing passkeys stronger, because authenticators still pick the first algorithm they support. Your server library also has to be able to verify every algorithm you list. If you add more than the standard three, run a regression test on old clients (especially Windows 10) before going live.

The IANA COSE registry now marks -7, -8, -35 and -36 as deprecated in favor of the fully specified identifiers from RFC 9864 (e.g. -19 for Ed25519). WebAuthn Level 3 explicitly recommends to keep using -7, -8, -35 and -36 instead of or in addition to the new identifiers, because most authenticators do not support them yet.

timeout#

"timeout": 300000

timeout is a hint in milliseconds for how long the browser should wait for the user to complete the ceremony. Browsers may ignore or cap it. WebAuthn Level 3 recommends a range of 300000 to 600000 ms with 300000 ms (five minutes) as default, because the user may need to unlock a phone via QR code or find a security key. Let the challenge on the server expire after the same time.

excludeCredentials#

"excludeCredentials": [ { "type": "public-key", "id": "Xk2b...9fQ", "transports": ["internal", "hybrid"] } ]

excludeCredentials lists credentials the user already has. If the authenticator already holds one of them, it refuses to create a second credential and the browser returns an InvalidStateError. This prevents duplicate passkeys for the same account in the same passkey provider. Each entry is a credential descriptor with the credential id, the type and optionally the stored transports. Always fill it with all of the user's existing credential IDs.

authenticatorSelection#

"authenticatorSelection": { "authenticatorAttachment": "platform", "residentKey": "required", "requireResidentKey": true, "userVerification": "required" }

authenticatorSelection defines the requirements an authenticator must meet.

  • authenticatorAttachment: "platform" restricts registration to the authenticator of the current device (Face ID, Touch ID, Windows Hello, Android screen lock). "cross-platform" restricts it to roaming authenticators such as security keys or a phone via QR code. If you leave it out, the user can choose any authenticator. This is a product decision: "platform" reduces enrollment to a single biometric prompt, while leaving it out adds a choice to the dialog but allows passkeys on a phone or security key.
  • residentKey: "required", "preferred" or "discouraged". If it is missing, the value of requireResidentKey decides, which means "discouraged" by default. Set it to "required" to create a discoverable credential. Only discoverable credentials work for usernameless login and Conditional UI. Read more about resident keys.
  • requireResidentKey: Legacy boolean from WebAuthn Level 1. Set it to true if residentKey is "required", otherwise false, for older browsers.
  • userVerification: "required", "preferred" (default) or "discouraged". "required" forces a biometric or PIN check. It also excludes security keys without a PIN or biometric sensor. See user verification for details.

Security keys used as a second factor after the password, without a PIN, need residentKey and userVerification set to "discouraged". If you support both passkeys and such security keys, treat them as two separate configurations and decide per user which one applies before you generate the options.

hints#

"hints": ["client-device"]

hints were added in WebAuthn Level 3. They tell the browser which UI to show first, in order of preference:

If hints contradict authenticatorAttachment, browsers that support hints follow the hints. Browsers that do not support hints ignore them, so set a matching authenticatorAttachment as well: "platform" together with "client-device" and "cross-platform" together with "security-key" or "hybrid".

attestation and attestationFormats#

"attestation": "none", "attestationFormats": []

attestation is a single string (not a list) that defines whether the authenticator should prove its make and model with an attestation statement:

  • "none" (default): No attestation statement. The best choice for consumer passkeys, because it causes no extra prompts and needs no certificate handling.
  • "indirect": The client may replace the attestation, e.g. with an anonymized one.
  • "direct": The Relying Party wants the attestation statement as generated by the authenticator. Needed if you want to verify the AAGUID cryptographically, e.g. to enforce an authenticator allowlist. Security key vendors such as Yubico recommend it for hardware key deployments.
  • "enterprise": Attestation that may uniquely identify the device. Only works for managed devices and RP IDs the browser or enterprise policy allows.

attestationFormats (Level 3) is an optional list of preferred attestation statement formats such as "packed", "tpm", "android-key" or "apple", in order of preference. Most implementations leave it empty.

Many server libraries let you configure a list of accepted attestation types. The options sent to the browser still contain only one attestation value.

Since WebAuthn Level 3 the browser no longer zeroes the AAGUID when you request "none". It only strips the attestation statement. So you usually still see which passkey provider created the credential (e.g. Google Password Manager), you just cannot verify it cryptographically. Showing this provider name in the user's account settings helps users find their passkey later. Synced passkeys from Apple and Google return no attestation statement even if you request "direct". If you want to restrict authenticators by AAGUID, decide up front how you treat passkeys without attestation. An allowlist blocks every authenticator you did not list, a denylist is easier to operate but cannot be enforced cryptographically for synced passkeys.

extensions#

"extensions": { "credProps": true }

extensions request additional processing. The most relevant ones for registration are:

  • credProps: Returns rk: true or false in the client extension results, so you know whether the created credential is discoverable. Useful when residentKey is "preferred".
  • prf: Lets the Relying Party derive secrets from the credential with a pseudo-random function, e.g. for end-to-end encryption.
  • largeBlob: Requests support for storing a small blob with the credential.
  • credentialProtectionPolicy (credProtect): Defines whether a security key may reveal the credential without user verification. See credProtect.
  • minPinLength: Returns the minimum PIN length of a security key.
  • appidExclude: Excludes credentials registered with the legacy FIDO U2F AppID.

Note that the backup eligibility (BE) and backup state (BS) values are not extensions. They are flags in the authenticator data of every response and tell you whether a passkey is syncable and synced. Store them together with the AAGUID for every registration and analyze them, instead of rejecting synced passkeys up front.

Passing the options from JSON#

Servers send the options as JSON with base64url encoded binary values. Instead of decoding them manually, you can use PublicKeyCredential.parseCreationOptionsFromJSON(), which is available since Chrome and Edge 129, Firefox 119 and Safari 18.4, and send the result back with credential.toJSON().

Debugger Icon

Experiment with passkey flows in the Passkeys Debugger.

Try for Free

PublicKeyCredentialCreationOptions FAQs#

What role do PublicKeyCredentialCreationOptions play in WebAuthn?#

They define every parameter of a WebAuthn registration: which Relying Party and user the credential belongs to, which algorithms are accepted, which authenticators may be used and whether attestation is requested. The server generates them with a fresh challenge and later verifies the response against them.

Which algorithms should I put in pubKeyCredParams?#

Include EdDSA (-8), ES256 (-7) and RS256 (-257). This is the set the WebAuthn specification recommends for the widest compatibility. Without RS256, older Windows Hello devices fail to register.

Should I set authenticatorAttachment to platform?#

Set it to "platform" if adoption is your main goal and users should create the passkey on the device they are using right now. Leave it out if users should also be able to register a security key or a passkey on their phone via QR code.

Can PublicKeyCredentialCreationOptions be customized for different authentication requirements?#

Yes. Some deployments use two configurations: one for passkeys (discoverable, user verification required) and one for security keys as a second factor (not discoverable, user verification discouraged).

Igor Gjorgjioski Testimonial

Igor Gjorgjioski

Senior Product Lead, VicRoads

We hit 80% mobile passkey activation across 5M+ users without replacing our IDP.

See how VicRoads scaled passkeys to 5M+ users, alongside their existing IDP.

Read the case study

What's the difference between PublicKeyCredentialCreationOptions and PublicKeyCredentialRequestOptions?#

Both are sent by the backend and contain a challenge. PublicKeyCredentialCreationOptions are used to create a new credential with navigator.credentials.create(), while PublicKeyCredentialRequestOptions are used to log in with an existing credential via navigator.credentials.get().

Which attestation value should I use: none, indirect, direct or enterprise?#

Use "none" for consumer passkeys, because it avoids extra prompts and certificate handling. Use "direct" if you need to verify the AAGUID cryptographically, e.g. to enforce an authenticator allowlist. "indirect" lets the browser replace or anonymize the attestation, which is rarely useful in practice. "enterprise" returns device-identifying attestation and only works on managed devices for RP IDs allowed by browser or enterprise policy. Apple and Google synced passkeys return no attestation statement, whatever value you request.

How does excludeCredentials prevent duplicate passkeys during WebAuthn registration?#

excludeCredentials lists credential IDs the user already registered. If the authenticator holds any of those credentials, it refuses to create a new one and the browser returns an InvalidStateError. Always populate it with all existing credential IDs for a user to prevent duplicate passkeys within the same passkey provider.

Why does authenticatorSelection have both residentKey and requireResidentKey fields in WebAuthn?#

requireResidentKey is a legacy boolean from WebAuthn Level 1 kept for backward compatibility with older browsers. residentKey is the modern string field introduced in Level 2, accepting "required", "preferred" or "discouraged". Set both consistently: requireResidentKey: true when residentKey is "required" and false otherwise.

How do WebAuthn Level 3 hints interact with authenticatorAttachment in PublicKeyCredentialCreationOptions?#

hints tell the browser which UI to show first: "client-device", "security-key" or "hybrid". When hints contradict authenticatorAttachment, Level 3 browsers follow the hints. Browsers without Level 3 support ignore hints entirely, so always set a matching authenticatorAttachment as a fallback: "platform" paired with "client-device" and "cross-platform" paired with "security-key" or "hybrid".

Corbado

About Corbado

Corbado is the Passkey Intelligence Platform for large-scale CIAM teams running consumer authentication. We help you see what IDP logs and generic analytics tools can't: where passkeys, passwords, OTP, social login and fallback journeys succeed, stall or fail, which devices and browsers create friction, and when an OS update silently breaks login. Two products: Corbado Observe layers process mining and observability across authentication journeys. Corbado Connect adds managed passkeys with analytics built in alongside your IDP. VicRoads runs passkeys for 5M+ users with Corbado (more than 80% passkey activation rate). Talk to a Passkey Expert →

See how Corbado fits your passkey rollout and existing authentication stack.

Explore the Console

Share this article


LinkedInTwitterFacebook