Passkey Authentication

Passkey Authentication

Nuxt Starter Kit includes built-in support for passkey authentication, providing a modern, secure, and user-friendly way to authenticate users without passwords.

What are Passkeys?

Passkeys are a modern authentication standard based on public-key cryptography. Instead of remembering and typing passwords, users can authenticate using their device's biometric sensors (Face ID, Touch ID, Windows Hello) or screen lock.

Benefits

More Secure

Resistant to phishing, credential stuffing, and password-related attacks. Private keys never leave the user's device.

Faster & Simpler

No passwords to remember or type. Authentication happens in seconds with a simple biometric scan or device unlock.

Cross-Device Support

Passkeys can be synced securely across devices through platform mechanisms like iCloud Keychain or Google Password Manager.

How It Works

Passkeys use WebAuthn (Web Authentication API), a W3C standard:

  1. Registration: When creating a passkey, a public-private key pair is generated on the user's device
  2. Storage: The private key stays securely on the device; the public key is stored on the server
  3. Authentication: When signing in, the server sends a challenge that only the private key can sign
  4. Verification: The server verifies the signed challenge using the stored public key

Using Passkeys

For Users

Registration

  1. Navigate to the Register or Login page
  2. Click the Passkey provider button (fingerprint icon)
  3. Enter your email address when prompted
  4. Follow your device's prompts (Face ID, Touch ID, Windows Hello, etc.)
  5. Your passkey is created and you're logged in!

Sign In

  1. Navigate to the Login page
  2. Click Sign In with Passkey
  3. If you have multiple passkeys, select the one you want to use
  4. Authenticate with your device's biometric or screen lock
  5. You're signed in!
You can also visit /auth/passkey for a dedicated passkey authentication page.

Browser Support

Passkeys are supported in all modern browsers:

  • Chrome/Edge: Version 67+
  • Safari: Version 13+
  • Firefox: Version 60+
  • All major mobile browsers (iOS Safari, Chrome Mobile, Samsung Internet)

For Developers

Architecture

The passkey implementation consists of three main parts:

  1. Client Components (in layers/auth/app/components/passkey/)
    • PasskeyRegister.vue - Registration form
    • PasskeyLogin.vue - Authentication button
    • PasskeyAuth.vue - Combined auth wrapper
  2. Server Endpoints (in layers/auth/server/api/webauthn/)
    • register.post.ts - Handles passkey registration
    • authenticate.post.ts - Handles passkey authentication
  3. Database Layer (in layers/db/server/utils/)
    • schema.ts - Credentials table schema
    • useCredentialDb.ts - Database operations for credentials

Database Schema

Passkey credentials are stored in the credentials table:

{
  userId: string // Foreign key to users table
  id: string // Credential ID (unique)
  publicKey: string // Public key for verification
  counter: number // Signature counter (replay protection)
  backedUp: boolean // Whether credential is backed up
  transports: string // Supported transports (USB, NFC, etc.)
}

API Endpoints

Registration: /api/webauthn/register

Handles new passkey creation:

// Client-side
const { register } = useWebAuthn()
await register({ userName: 'user@example.com' })

Authentication: /api/webauthn/authenticate

Handles passkey sign-in:

// Client-side
const { authenticate } = useWebAuthn()
await authenticate() // Can optionally pass userName to skip selection

Configuration

WebAuthn is enabled in layers/auth/nuxt.config.ts:

export default defineNuxtConfig({
  auth: {
    webAuthn: true,
  },
  modules: ['nuxt-auth-utils'],
})

Security Features

  1. Replay Attack Prevention: Signature counter is verified and updated on each authentication
  2. Challenge-Response: Single-use challenges are stored and validated
  3. Email Verification: Users created via passkey have emailVerified set to true by default
  4. Secure Session Management: Built-in session handling via nuxt-auth-utils

Integration Example

Add passkey authentication to your own auth page:

<script setup lang="ts">
const { authenticate } = useWebAuthn()
const { fetch: fetchUserSession } = useUserSession()

async function handlePasskeyAuth () {
  await authenticate()
  await fetchUserSession()
  // User is now authenticated!
}
</script>

<template>
  <UButton
    icon="i-lucide-fingerprint"
    @click="handlePasskeyAuth"
  >
    Sign in with Passkey
  </UButton>
</template>

Technical Details

Dependencies

  • @simplewebauthn/server@11 - Server-side WebAuthn library
  • @simplewebauthn/browser@11 - Client-side WebAuthn library
  • nuxt-auth-utils - Nuxt authentication utilities with WebAuthn support

Migration

The credentials table migration is included in the database migrations. Run:

pnpm db:generate  # Already generated

Migrations are automatically applied in development mode.

Composables

The useWebAuthn() composable is auto-imported from nuxt-auth-utils and provides:

  • register(options) - Create a new passkey
  • authenticate(userName?) - Authenticate with an existing passkey

Best Practices

  1. Offer Multiple Auth Methods: Keep password and OAuth options alongside passkeys
  2. Clear Communication: Explain what passkeys are to users who may be unfamiliar
  3. Graceful Degradation: Handle cases where passkeys aren't supported
  4. Multiple Passkeys: Allow users to register multiple passkeys per account
  5. Recovery Options: Provide alternative authentication methods for account recovery

Troubleshooting

"Passkey not found" Error

  • Ensure the user has registered a passkey for this site
  • Check browser compatibility
  • Verify the domain matches the registration domain

Registration Fails

  • Confirm WebAuthn is enabled in your Nuxt config
  • Check that the database migrations have been applied
  • Verify the user's device supports biometric authentication

Cross-Device Issues

  • Not all platforms support cross-device passkey sync yet
  • Users may need to register separate passkeys on each device
  • QR code flow allows using a passkey from another device

Learn More