Passkeys make user accounts safer, simpler, easier to use.
Published: October 12, 2022, Last updated: April 09, 2026
Using passkeys enhances security, simplifies logins, and replaces passwords. Unlike regular passwords, which users must remember and enter manually, passkeys use device's screen lock mechanisms like biometrics or PINs and reduce phishing risks and credential theft.
Passkeys sync across devices using passkey providers like Google Password Manager and iCloud Keychain.
A passkey must be created, storing the private key securely to the passkey provider along with necessary metadata and its public key stored on your server for authentication. The private key issues a signature after user verification on the valid domain making passkeys phishing resistant. The public key verifies the signature without storing sensitive credentials, making passkeys resistant to credential theft.
How creating a passkey works
Before a user can sign in with a passkey, you should create the passkey, associate it with a user account, and store its public key on your server.
You could ask users to create a passkey in one of the following situations:
- During or after sign up.
- After signing in.
- After signing in using a passkey from another device (that is, the
authenticatorAttachmentiscross-platform). - On a dedicated page where users can manage their passkeys.
To create a passkey, you use the WebAuthn API.
The four components of the passkey registration flow are:
- Backend: Stores user account details, including the public key.
- Frontend: Communicates with the browser and fetches necessary data from the backend.
- Browser: Runs your JavaScript and interacts with the WebAuthn API.
- Passkey provider: Creates and stores the passkey. This is typically a password manager such as Google Password Manager, or a security key.
Before creating a passkey, ensure that the system meets these prerequisites:
The user account is verified through a secure method (for example, email, phone verification, or identity federation) within a meaningfully short window.
The frontend and backend can communicate securely to exchange credential data.
The browser supports WebAuthn and passkey creation.
We can show you how to check most of them in the following sections.
Once the system meets this conditions, the following process happens to create a passkey:
- The system triggers the passkey creation process when the user initiates the action (for example, clicking a "Create a Passkey" button in their passkey management page or after finishing their registration).
- The frontend requests necessary credential data from the backend, including user information, a challenge, and credential IDs to prevent duplicates.
- The frontend calls
navigator.credentials.create()to prompt the device's passkey provider to generate a passkey using the information from the backend. Note that this call returns a promise. - The user's device authenticates the user using a biometric method, PIN, or pattern to create the passkey.
- The passkey provider creates a passkey and returns a public key credential to the frontend, resolving the promise.
- The frontend sends the generated public key credential to the backend.
- The backend stores the public key and other important data for future authentication,
- The backend notifies the user (for example, using email) to confirm the passkey creation and detect potential unauthorized access.
This process ensures a secure and seamless passkey registration process for users.
Compatibilities
Most browsers support WebAuthn, with some minor gaps. See passkeys.dev for browser and OS compatibility details.
Create a new passkey
To create a new passkey, this is the process the frontend should follow:
- Check for compatibility.
- Fetch information from the backend.
- Call WebAuth API to create a passkey.
- Send the returned public key to the backend.
- Save the credential.
The following sections show how you can do it.
Check for compatibility
Before displaying a "Create a new passkey" button, the frontend should check if:
- The browser supports WebAuthn with
PublicKeyCredential.
- The browser supports capability
detection with
PublicKeyCredential.getClientCapabilities().
The browser supports WebAuthn conditional UI with
conditionalGet.The device supports a platform authenticator (can create a passkey and authenticate on the device) with
passkeyPlatformAuthenticator.
The following code snippet shows how you can check for compatibility before displaying the passkey-related options.
if (window.PublicKeyCredential && PublicKeyCredential.getClientCapabilities) {
const capabilities = await PublicKeyCredential.getClientCapabilities();
if (capabilities.conditionalGet === true &&
capabilities.passkeyPlatformAuthenticator === true) {
// The browser supports passkeys and the conditional UI.
}
}
In this example, the Create a new passkey button should only be displayed if all the conditions are met.
Fetch information from the backend
When the user clicks the button, fetch the required information from the
backend to call navigator.credentials.create().
The following code snippet shows a JSON object with the required information to
call navigator.credentials.create():
// Example `PublicKeyCredentialCreationOptions` contents
{
challenge: *****,
rp: {
name: "Example",
id: "example.com",
},
user: {
id: *****,
name: "john78",
displayName: "John",
},
pubKeyCredParams: [{
alg: -7, type: "public-key"
},{
alg: -257, type: "public-key"
}],
excludeCredentials: [{
id: *****,
type: 'public-key',
transports: ['internal'],
}],
authenticatorSelection: {
authenticatorAttachment: "platform",
requireResidentKey: true,
}
}
The key-value pairs in the object hold the following information:
challenge: A server-generated challenge in ArrayBuffer for this registration.rp.id: An RP ID (Relying Party ID), a domain and a website can specify either its domain or a registrable suffix. For example, if an RP's origin ishttps://login.example.com:1337, the RP ID can be eitherlogin.example.comorexample.com. If the RP ID is specified asexample.com, the user can authenticate onlogin.example.comor on any subdomains onexample.com. See, Allow passkey reuse across your sites with Related Origin Requests for more information on this.rp.name: The RP's (Relying Party) name. This is deprecated in WebAuthn L3 but included for compatibility reasons.user.id: A unique user ID in ArrayBuffer, generated upon account creation. It should be permanent, unlike a username that may be editable. The user ID identifies an account, but should not contain any personally identifiable information (PII). You likely already have a user ID in your system, but if needed, create one specifically for passkeys to keep it free of any PII.user.name: A unique identifier for the account that the user will recognise, like their email address or username. This will be displayed in the account selector.user.displayName: A required, more user-friendly name for the account. It need not be unique and could be the user's chosen name. If your site does not have a suitable value to include here, pass an empty string. This may be displayed on the account selector depending on the browser.pubKeyCredParams: Specifies the RP (relying party) supported public-key algorithms. We recommend setting it to[{alg: -7, type: "public-key"},{alg: -257, type: "public-key"}]. This specifies support for ECDSA with P-256 and RSA PKCS#1 and supporting these gives complete coverage.excludeCredentials: A list of already registered credential IDs. Prevents registering the same device twice by providing a list of already registered credential IDs. Thetransportsmember, if provided, should contain the result of callinggetTransports()during the registration of each credential.authenticatorSelection.authenticatorAttachment: Set this to"platform"along withhint: ['client-device']if this passkey creation is an upgrade from a password for example in a promotion after a sign-in."platform"indicates that the RP wants a platform authenticator (an authenticator embedded to the platform device) which does not prompt, for example, to insert a USB security key. The user has a simpler option to create a passkey.authenticatorSelection.requireResidentKey: Set it to a booleantrue. A discoverable credential (resident key) stores user information to the passkey and lets users select the account upon authentication.authenticatorSelection.userVerification: Indicates whether a user verification using the device screen lock is"required","preferred"or"discouraged". The default is"preferred", which means the authenticator may skip user verification. Set this to"preferred"or omit the property.
We recommend constructing the object on the server, encoding the ArrayBuffer
with Base64URL and fetching it from the frontend. This way, you can decode the
payload using PublicKeyCredential.parseCreationOptionsFromJSON() and pass it
directly to navigator.credentials.create().
The following code snippet shows how you can fetch and decode the information needed to create the passkey.
// Fetch an encoded `PubicKeyCredentialCreationOptions` from the server.
const _options = await fetch('/webauthn/registerRequest');
// Deserialize and decode the `PublicKeyCredentialCreationOptions`.
const decoded_options = JSON