šŸ“ Sign Up | šŸ” Log In

← Root | ↑ Up

ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā” │ šŸ“„ shadcn/directory/clerk/clerk-docs/guides/development/custom-flows/authentication/passkeys │ ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

╔══════════════════════════════════════════════════════════════════════════════════════════════╗
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘

title: Build a custom authentication flow using passkeys description: Learn how to use the Clerk API to build a custom authentication flow using passkeys.

<Include src="_partials/custom-flows-callout" />

Clerk supports passwordless authentication via passkeys, enabling users to sign in without having to remember a password. Instead, users select a passkey associated with their device, which they can use to authenticate themselves.

This guide demonstrates how to use the Clerk API to build a custom user interface for creating, signing users in with, and managing passkeys.

Enable passkeys

To use passkeys, first enable the strategy in the Clerk Dashboard.

<If sdk="android"> When setting up passkeys with Android, there are a few additional steps to follow. [Learn more about passkeys for Android](/docs/reference/android/passkeys). </If> <If sdk="expo"> When setting up passkeys with Expo, there are a few additional steps to follow. [Learn more about passkeys for Expo](/docs/reference/expo/passkeys). </If>

Domain restrictions for passkeys

<Include src="_partials/authentication/passkeys-restriction" />

Create user passkeys

To create a passkey for a user, you must call User.createPasskey(), as shown in the following example:

export function CreatePasskeyButton() {
  const { isSignedIn, user } = useUser()

  const createClerkPasskey = async () => {
    if (!isSignedIn) {
      // Handle signed out state
      return
    }

    try {
      await user?.createPasskey()
    } catch (err) {
      // See https://clerk.com/docs/guides/development/custom-flows/error-handling
      // for more info on error handling
      console.error('Error:', JSON.stringify(err, null, 2))
    }
  }

  return <button onClick={createClerkPasskey}>Create a passkey</button>
}

Sign a user in with a passkey

To sign a user into your Clerk app with a passkey, you must call SignIn.authenticateWithPasskey(). This method allows users to choose from their discoverable passkeys, such as hardware keys or passkeys in password managers.

export function SignInWithPasskeyButton() {
  const { signIn } = useSignIn()
  const router = useRouter()

  const signInWithPasskey = async () => {
    // 'discoverable' lets the user choose a passkey
    // without auto-filling any of the options
    try {
      const signInAttempt = await signIn?.authenticateWithPasskey({
        flow: 'discoverable',
      })

      if (signInAttempt?.status === 'complete') {
        await setActive({
          session: signInAttempt.createdSessionId,
          redirectUrl: '/',
          navigate: async ({ session }) => {
            if (session?.currentTask) {
              // Check for tasks and navigate to custom UI to help users resolve them
              // See https://clerk.com/docs/guides/development/custom-flows/overview#session-tasks
              console.log(session?.currentTask)
              return
            }

            router.push('/')
          },
        })
      } else {
        // If the status is not complete, check why. User may need to
        // complete further steps.
        console.error(JSON.stringify(signInAttempt, null, 2))
      }
    } catch (err) {
      // See https://clerk.com/docs/guides/development/custom-flows/error-handling
      // for more info on error handling
      console.error('Error:', JSON.stringify(err, null, 2))
    }
  }

  return <button onClick={signInWithPasskey}>Sign in with a passkey</button>
}

Rename user passkeys

Clerk generates a name based on the device associated with the passkey when it's created. Sometimes users may want to rename a passkey to make it easier to identify.

To rename a user's passkey in your Clerk app, you must call the update() method of the passkey object, as shown in the following example:

export function RenamePasskeyUI() {
  const { user } = useUser()
  const { passkeys } = user

  const passkeyToUpdateId = useRef<HTMLInputElement>(null)
  const newPasskeyName = useRef<HTMLInputElement>(null)
  const [success, setSuccess] = useState(false)

  const renamePasskey = async () => {
    try {
      const passkeyToUpdate = passkeys?.find(
        (pk: PasskeyResource) => pk.id === passkeyToUpdateId.current?.value,
      )

      await passkeyToUpdate?.update({
        name: newPasskeyName.current?.value,
      })

      setSuccess(true)
    } catch (err) {
      // See https://clerk.com/docs/guides/development/custom-flows/error-handling
      // for more info on error handling
      console.error('Error:', JSON.stringify(err, null, 2))
      setSuccess(false)
    }
  }

  return (
    <>
      <p>Passkeys:</p>
      <ul>
        {passkeys?.map((pk: PasskeyResource) => {
          return (
            <li key={pk.id}>
              Name: {pk.name} | ID: {pk.id}
            </li>
          )
        })}
      </ul>
      <input ref={passkeyToUpdateId} type="text" placeholder="Enter the passkey ID" />
      <input type="text" placeholder="Enter the passkey's new name" ref={newPasskeyName} />
      <button onClick={renamePasskey}>Rename passkey</button>
      <p>Passkey updated: {success ? 'Yes' : 'No'}</p>
    </>
  )
}

Delete user passkeys

To delete a user's passkey from your Clerk app, you must call the delete() method of the passkey object, as shown in the following example:

export function DeletePasskeyUI() {
  const { user } = useUser()
  const { passkeys } = user

  const passkeyToDeleteId = useRef<HTMLInputElement>(null)
  const [success, setSuccess] = useState(false)

  const deletePasskey = async () => {
    const passkeyToDelete = passkeys?.find((pk: any) => pk.id === passkeyToDeleteId.current?.value)
    try {
      await passkeyToDelete?.delete()

      setSuccess(true)
    } catch (err) {
      // See https://clerk.com/docs/guides/development/custom-flows/error-handling
      // for more info on error handling
      console.error('Error:', JSON.stringify(err, null, 2))
      setSuccess(false)
    }
  }

  return (
    <>
      <p>Passkeys:</p>
      <ul>
        {passkeys?.map((pk: any) => {
          return (
            <li key={pk.id}>
              Name: {pk.name} | ID: {pk.id}
            </li>
          )
        })}
      </ul>
      <input ref={passkeyToDeleteId} type="text" placeholder="Enter the passkey ID" />
      <button onClick={deletePasskey}>Delete passkey</button>
      <p>Passkey deleted: {success ? 'Yes' : 'No'}</p>
    </>
  )
}
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•

← Root | ↑ Up