Skip to main content

Creating Passkey Wallets

info

Our passkey integration with Turnkey (see below) has been deprecated. We recommend new projects to use Dynamic + ZeroDev instead.

Demo

Intro

Passkeys are becoming an increasingly popular alternative to passwords. With passkeys, users can use biometrics such as fingerprints and facial recognition to log into apps.

In the context of ZeroDev, passkeys can be used as the signers/owners for your AA wallets. This enables a powerful experience where your users get to create self-custody AA wallets using biometrics, without having to remember any seed phrases.

Installing Dependencies

Make sure you have installed the ZeroDev SDK and a helper package:

npm i @zerodev/sdk @turnkey/http

Or if you are using ZeroDev with Wagmi:

npm i @zerodev/wagmi @turnkey/http

We will be using the Wagmi package in the following examples, since passkeys are most commonly used in the frontend.

Getting Started

To use a passkey to create an AA wallet, use one of our helper functions to create a passkey "owner," then use the owner to initialize the wallet. Here's a minimal example:

import { ZeroDevConnector } from "@zerodev/wagmi"
import { createPasskeyOwner } from "@zerodev/sdk/passkey"
import { useConnect, configureChains } from "wagmi"

export const { chains } = configureChains(
// make sure to specify a chain that corresponds to your ZeroDev project
)
const projectId = 'YOUR_ZERODEV_PROJECT_ID'

function AuthenticateScreen() {
const handleRegister = async () => {
connect({
connector: new ZeroDevConnector({
chains, options: {
projectId,
owner: await createPasskeyOwner({ name: 'Name of your app', projectId })
}
})
})
}

const handleLogin = async () => {
connect({
connector: new ZeroDevConnector({
chains, options: {
projectId,
owner: await getPasskeyOwner({ projectId })
}
})
})
}

return (
<div>
<button onClick={handleRegister}> Register </button>
<button onClick={handleLogin}> Login </button>
</div>
)
}

As you see from the example, there are two ways to create passkey owners:

  • createPasskeyOwner({ name, projectId }) creates a passkey with a given label (name). This function is commonly used to "sign up" for your app since it creates a new passkey.
Full Code (Editable)
Result
Loading...
  • getPasskeyOwner({ projectId }) asks the user to select an existing passkey. This function is commonly used to "log into" your app since it asks the user to select an existing passkey.
Full Code (Editable)
Result
Loading...

Security

This section is for people who want to understand the full security implications of using passkeys with AA wallets.

Generally speaking, there are two ways to achieve "passkey AA wallets:"

  1. Program the AA wallets to verify passkey signatures on-chain
  2. Use passkeys to authenticate with a signing service that can produce ECDSA signatures

The first approach, natively verifying passkey signatures on-chain, is the ideal one, since it allows users to sign transactions directly with passkeys, without relying on any intermediaries. However, this approach is currently impractical -- the best on-chain passkey implementation that we know of costs about 300k gas per transaction, which is prohibitively expensive for most applications. In order to lower the gas cost, EVM itself likely needs to be upgraded with something like EIP-7212.

The second approach is the one that ZeroDev currently takes. When a user creates a passkey AA wallet, we are actually using the passkey to authenticate against a third-party signing service called Turnkey.

Turnkey's infra is based on secure enclaves. Critically, Turnkey's infra ensures that no party, not even Turnkey itself, can access the user's private key without the proper credential -- which in this case is the user's passkey.

In other words, we are not actually using the passkeys themselves to sign transactions. Rather, we are using the passkeys to unlock ECDSA private keys, and then use the ECDSA private keys to sign transactions. By doing so, we achieve maximal compatibility with existing wallet infra as well gas efficiency, with the caveat that we are relying on a centralized signing service (Turnkey).

While the word "centralized" invokes uneasy feelings, we think that this could still be a good solution for many projects, for the following reasons:

  • As previously stated, Turnkey itself cannot access the private keys. Only the user with the passkey can access their own private key.
  • Turnkey's infra runs on AWS, so it enjoys high availability and redundancy.
  • Since ZeroDev is an AA wallet, it's always possible to switch the signer from Turnkey to another signer (e.g. native passkeys), should the user decide to secure their assets with a different approach.