Skip to main content

WebAuthn & Passkeys

Deep dive into Signia's passwordless authentication implementation using WebAuthn and Passkeys.

What is WebAuthn?

WebAuthn (Web Authentication) is a W3C standard that enables passwordless authentication using public-key cryptography. It's the underlying technology behind Passkeys.

Key Benefits

  • 🔐 Phishing-resistant - Cryptographic proof of origin
  • Fast - Biometric authentication in seconds
  • 🔑 No passwords - Nothing to remember or type
  • 🌐 Cross-platform - Works across devices and browsers
  • 🔒 Private - Biometrics never leave your device

How It Works

Registration Flow

What happens:

  1. Server generates a random challenge
  2. Device creates a new key pair (private + public)
  3. Private key is stored securely on device (never shared)
  4. Public key is sent to server and stored
  5. User proves possession via biometric

Authentication Flow

What happens:

  1. Server generates authentication challenge
  2. Device signs challenge with private key
  3. Signature is sent to server
  4. Server verifies signature using stored public key
  5. If valid, user is authenticated

Passkeys vs Traditional WebAuthn

Traditional WebAuthn

  • Device-bound - Credential tied to single device
  • No sync - Must register each device separately
  • Recovery challenge - Lose device = lose access

Passkeys (Modern WebAuthn)

  • Synchronized - Credential synced across devices via OS
  • Multi-device - Works on all your Apple/Google/Microsoft devices
  • Better UX - Seamless cross-device authentication
  • Platform support:
    • Apple: iCloud Keychain
    • Google: Google Password Manager
    • Microsoft: Windows Hello
What Signia Uses

Signia supports both traditional WebAuthn and modern Passkeys. The implementation automatically uses passkeys when available, providing the best possible user experience.

Implementation in Signia

Registration

// Backend generates challenge
POST /register/start
{
"username": "user@example.com",
"display_name": "John Doe"
}

Response:
{
"challenge": "randomBase64String...",
"user": {
"id": "userId",
"name": "user@example.com",
"displayName": "John Doe"
},
"rp": {
"name": "Signia",
"id": "signiaauth.com"
},
"pubKeyCredParams": [
{ "type": "public-key", "alg": -7 } // ES256
],
"timeout": 60000,
"authenticatorSelection": {
"authenticatorAttachment": "platform", // Prefer platform (Face ID/Touch ID)
"residentKey": "preferred", // Enable passkeys
"userVerification": "required" // Require biometric
}
}
// Frontend creates credential
const credential = await navigator.credentials.create({
publicKey: options
});

// Send to backend
POST /register/finish
{
"credential": credential,
"challenge": "originalChallenge"
}

Authentication

// Backend generates authentication challenge
POST /auth/start
{
"username": "user@example.com" // Optional for passkeys
}

Response:
{
"challenge": "randomBase64String...",
"allowCredentials": [
{
"type": "public-key",
"id": "credentialId..."
}
],
"timeout": 60000,
"userVerification": "required"
}
// Frontend gets assertion
const assertion = await navigator.credentials.get({
publicKey: options
});

// Send to backend
POST /auth/finish
{
"assertion": assertion,
"challenge": "originalChallenge"
}

Security Features

Challenge-Response

Every authentication uses a unique challenge:

const challenge = crypto.randomBytes(32);  // 32 random bytes
// Challenge is valid for 60 seconds only

Prevents:

  • Replay attacks
  • Man-in-the-middle attacks

Origin Binding

Credentials are bound to the origin:

rp: {
id: "signiaauth.com",
name: "Signia"
}

Prevents:

  • Phishing (credential won't work on fake site)
  • Cross-origin attacks

User Verification

Biometric authentication required:

authenticatorSelection: {
userVerification: "required" // Must use biometric
}

Options:

  • Face ID (iOS, macOS)
  • Touch ID (macOS, iOS)
  • Windows Hello (Windows)
  • Fingerprint (Android)

Attestation

Verify the authenticator is legitimate:

attestation: "direct"  // Request attestation from authenticator

Attestation types:

  • None - No attestation
  • Indirect - Anonymized attestation
  • Direct - Full attestation with device info
Privacy vs Security

Signia uses "none" or "indirect" attestation by default to protect user privacy while maintaining security.

Platform Support

Desktop

PlatformBrowserSupportFeatures
macOSSafari✅ ExcellentTouch ID, iCloud Keychain sync
macOSChrome✅ ExcellentTouch ID, Google Password Manager
macOSFirefox✅ GoodTouch ID
WindowsEdge✅ ExcellentWindows Hello, Microsoft sync
WindowsChrome✅ ExcellentWindows Hello, Google sync
WindowsFirefox✅ GoodWindows Hello
LinuxChrome⚠️ LimitedUSB security keys only
LinuxFirefox⚠️ LimitedUSB security keys only

Mobile

PlatformBrowserSupportFeatures
iOSSafari✅ ExcellentFace ID, Touch ID, iCloud sync
iOSChrome✅ ExcellentFace ID, Touch ID, iCloud sync
AndroidChrome✅ ExcellentFingerprint, Google sync
AndroidFirefox✅ GoodFingerprint

Credential Types

Platform Authenticators

Characteristics:

  • Built into device (Face ID, Touch ID, Windows Hello)
  • Cannot be removed or transferred
  • Highest security
  • Best user experience

Use case: Primary authentication method

Roaming Authenticators

Characteristics:

  • Physical security keys (YubiKey, Titan Key)
  • Can be used on multiple devices
  • Requires carrying physical key
  • Excellent for backup

Use case: Backup authentication or high-security scenarios

Security Key Recommendations

Consumer:

  • YubiKey 5 Series
  • Google Titan Security Key
  • Feitian ePass K9

Enterprise:

  • YubiKey 5 FIPS
  • Feitian A4 FIPS
  • Token2 FIDO2 keys

Advanced Features

Resident Keys (Discoverable Credentials)

Enable username-less login:

authenticatorSelection: {
residentKey: "required" // Store credential on device
}

Benefits:

  • No need to enter username
  • Faster login flow
  • Better UX

Requirements:

  • Device must support resident keys
  • Sufficient storage on authenticator

Multiple Credentials

Users can register multiple credentials:

// User's credentials
[
{
id: "cred1",
type: "platform",
device: "iPhone 13",
created: "2024-01-15"
},
{
id: "cred2",
type: "roaming",
device: "YubiKey 5",
created: "2024-01-20"
}
]

Best practice: Register at least 2 credentials:

  • Primary: Device passkey (Face ID/Touch ID)
  • Backup: Security key

Credential Management

// List user's credentials
GET /api/credentials

// Revoke a credential
DELETE /api/credentials/:id

// Rename a credential
PATCH /api/credentials/:id
{
"name": "Work iPhone"
}

Testing WebAuthn

Browser DevTools

Chrome DevTools:

  1. Open DevTools (F12)
  2. Go to Application tab
  3. Find WebAuthn section
  4. Add virtual authenticator

Options:

  • Protocol: CTAP2
  • Transport: Internal
  • Supports resident keys: ✅
  • Supports user verification: ✅

Testing Tools

Libraries:

  • @github/webauthn-json - Simplified WebAuthn API
  • @simplewebauthn/browser - WebAuthn utilities
  • webauthn-get - CLI testing tool

Online Testers:

Troubleshooting

"Not allowed" error

Causes:

  • User cancelled prompt
  • Timeout expired
  • Invalid origin
  • Browser doesn't support WebAuthn

Solutions:

  • Check browser compatibility
  • Ensure HTTPS (required)
  • Verify origin matches RP ID

Passkey not syncing

iOS/macOS:

  • Ensure iCloud Keychain enabled
  • Check Apple ID sync settings
  • Wait a few minutes for sync

Android:

  • Ensure Google Password Manager enabled
  • Check Google account sync
  • Update Google Play Services

Biometric not working

Checks:

  • Biometric enrolled on device
  • Biometric permissions granted
  • Device supports WebAuthn
  • Browser supports user verification

Best Practices

For Users

  1. Register multiple credentials

    • Primary device
    • Backup device
    • Security key
  2. Keep devices updated

    • Latest OS updates
    • Latest browser versions
  3. Protect your devices

    • Use strong device PIN/password
    • Enable device encryption

For Developers

  1. Always use HTTPS

    • Required for WebAuthn
    • Use localhost for local dev
  2. Set appropriate timeouts

    • 60 seconds for registration
    • 60 seconds for authentication
  3. Handle errors gracefully

    • Provide clear error messages
    • Offer fallback options
  4. Test across platforms

    • Different browsers
    • Different operating systems
    • Different devices

Further Reading

Next Steps