Skip to content

Embed Passkey Accounts

Create a domain-bound passkey account on Tempo using WebAuthn signatures for secure, passwordless authentication with Tempo transactions.

Passkeys enable users to authenticate with biometrics (fingerprint, Face ID, Touch ID) they already use for other apps. Keys are stored in the device's secure enclave and sync across devices via iCloud Keychain or Google Password Manager.

Demo

By the end of this guide, you will be able to embed passkey accounts into your application.

Passkey Accounts

demo
pnpx gitpick tempoxyz/tempo-ts/tree/main/examples/accounts

Steps

Set up Wagmi

Ensure that you have set up your project with Wagmi by following the guide.

Configure the WebAuthn Connector

Next, we will need to configure the webAuthn connector in our Wagmi config.

config.ts
import { createConfig, http } from 'wagmi'
import { tempo } from 'tempo.ts/chains'
import { KeyManager, webAuthn } from 'tempo.ts/wagmi'
 
export const config = createConfig({
  chains: [tempo({ feeToken: '0x20c0000000000000000000000000000000000001' })],
  connectors: [webAuthn({ 
    keyManager: KeyManager.localStorage(), 
  })], 
  multiInjectedProviderDiscovery: false,
  transports: {
    [tempo.id]: http(),
  },
})

Display Sign In Buttons

After that, we will set up "Sign in" and "Sign up" buttons so that the user can create or use a passkey with the application.

We will create a new Example.tsx component to work in.

Example.tsx
import { useConnect, useConnectors } from 'wagmi'
 
export function Example() {
  const connect = useConnect()
  const [connector] = useConnectors()
 
  return (
    <div>
      <button
        onClick={() =>
          connect.connect({
            connector,
            capabilities: { type: 'sign-up' },
          })
        }
      >
        Sign up
      </button>
 
      <button onClick={() => connect.connect({ connector })}>
        Sign in
      </button>
    </div>
  )
}

Display Account & Sign Out

After the user has signed in, we can display the account information and a sign out button.

Example.tsx
import { useConnection, useConnect, useConnectors, useDisconnect } from 'wagmi'
 
export function Example() {
  const account = useConnection() 
  const connect = useConnect()
  const [connector] = useConnectors()
  const disconnect = useDisconnect() 
 
  if (account.address) 
    return ( 
      <div> 
        <div>{account.address.slice(0, 6)}...{account.address.slice(-4)}</div> 
        <button onClick={() => disconnect.disconnect()}>Sign out</button> 
      </div> 
    ) 
 
  return (
    <div>
      <button
        onClick={() =>
          connect.connect({
            connector,
            capabilities: { type: 'sign-up' },
          })
        }
      >
        Sign up
      </button>
 
      <button onClick={() => connect.connect({ connector })}>
        Sign in
      </button>
    </div>
  )
}

Next Steps

Now that you have created your first passkey account, you can now:

Best Practices

Loading State

When the user is logging in or signing out, we should show loading state to indicate that the process is happening.

We can use the isPending property from the useConnect hook to show pending state to the user.

Example.tsx
import { useConnection, useConnect, useConnectors, useDisconnect } from 'wagmi'
 
export function Example() {
  const account = useConnection()
  const connect = useConnect()
  const [connector] = useConnectors()
  const disconnect = useDisconnect()
 
  if (connect.isPending) 
    return <div>Check prompt...</div> 
  return (/* ... */)
}

Error Handling

If an error unexpectedly occurs, we should display an error message to the user.

We can use the error property from the useConnect hook to show error state to the user.

Example.tsx
import { useConnection, useConnect, useConnectors, useDisconnect } from 'wagmi'
 
export function Example() {
  const account = useConnection()
  const connect = useConnect()
  const [connector] = useConnectors()
  const disconnect = useDisconnect()
 
  if (connect.error) 
    return <div>Error: {connect.error.message}</div> 
  return (/* ... */)
}

Learning Resources