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:
- Registration: When creating a passkey, a public-private key pair is generated on the user's device
- Storage: The private key stays securely on the device; the public key is stored on the server
- Authentication: When signing in, the server sends a challenge that only the private key can sign
- Verification: The server verifies the signed challenge using the stored public key
Using Passkeys
For Users
Registration
- Navigate to the Register or Login page
- Click the Passkey provider button (fingerprint icon)
- Enter your email address when prompted
- Follow your device's prompts (Face ID, Touch ID, Windows Hello, etc.)
- Your passkey is created and you're logged in!
Sign In
- Navigate to the Login page
- Click Sign In with Passkey
- If you have multiple passkeys, select the one you want to use
- Authenticate with your device's biometric or screen lock
- You're signed in!
/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:
- Client Components (in
layers/auth/app/components/passkey/)PasskeyRegister.vue- Registration formPasskeyLogin.vue- Authentication buttonPasskeyAuth.vue- Combined auth wrapper
- Server Endpoints (in
layers/auth/server/api/webauthn/)register.post.ts- Handles passkey registrationauthenticate.post.ts- Handles passkey authentication
- Database Layer (in
layers/db/server/utils/)schema.ts- Credentials table schemauseCredentialDb.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
- Replay Attack Prevention: Signature counter is verified and updated on each authentication
- Challenge-Response: Single-use challenges are stored and validated
- Email Verification: Users created via passkey have
emailVerifiedset totrueby default - 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 librarynuxt-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 passkeyauthenticate(userName?)- Authenticate with an existing passkey
Best Practices
- Offer Multiple Auth Methods: Keep password and OAuth options alongside passkeys
- Clear Communication: Explain what passkeys are to users who may be unfamiliar
- Graceful Degradation: Handle cases where passkeys aren't supported
- Multiple Passkeys: Allow users to register multiple passkeys per account
- 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
- WebAuthn Guide - Comprehensive WebAuthn documentation
- Passkeys.dev - Learn about passkeys from Google
- Nuxt Auth Utils - Documentation for nuxt-auth-utils
- Original Tutorial - Vue School passkey tutorial