Skip to content

A comprehensive guide to understanding and implementing FIDO Passkey WebAuthn authentication.

Notifications You must be signed in to change notification settings

harryosmar/webauthn

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

FIDO Passkey WebAuthn

A comprehensive guide to understanding and implementing FIDO Passkey WebAuthn authentication.

🚀 Quick Start Guide - Get the demo running in 5 minutes!

Table of Contents

Quick Start

# Install dependencies
go mod download

# Run the server
go run main.go

# Open browser
open http://localhost:8080

For detailed instructions, see QUICKSTART.md.

What is FIDO WebAuthn?

FIDO (Fast IDentity Online) is an authentication standard that enables passwordless authentication. WebAuthn is the W3C web standard that implements FIDO2 in web browsers, allowing websites to use public key cryptography for user authentication instead of passwords.

Core Concepts

Passkeys

  • Modern replacement for passwords: Cryptographic credentials that are more secure and user-friendly
  • Cryptographic key pairs: Private key stored securely on device, public key stored on server
  • Phishing-resistant: Keys are bound to specific domains
  • Synced across devices: Via platform providers (Apple iCloud Keychain, Google Password Manager, etc.)
  • Multi-device support: Use passkeys across your phones, tablets, and computers

Key Components

1. Relying Party (RP)

Your web application/server that:

  • Initiates registration and authentication ceremonies
  • Stores public keys and credential IDs
  • Verifies authentication responses
  • Manages user sessions

2. Authenticator

User's device (phone, laptop, security key) that:

  • Generates and stores private keys in secure hardware
  • Signs challenges during authentication
  • Performs user verification (biometrics, PIN)
  • Can be platform authenticators (built-in Touch ID, Face ID, Windows Hello) or roaming authenticators (USB security keys like YubiKey)

3. Client

Web browser that:

  • Mediates between RP and authenticator
  • Implements WebAuthn JavaScript API (navigator.credentials)
  • Handles user consent and UI

Registration Flow

The registration ceremony creates a new credential for a user.

sequenceDiagram
    participant User
    participant Browser
    participant Server
    participant Authenticator

    User->>Browser: Click "Register"
    Browser->>Server: Request registration options
    Server->>Server: Generate random challenge
    Server->>Server: Choose attestation level<br/>(none/indirect/direct/enterprise)
    Server->>Browser: Return challenge + RP info + user info + attestation level
    Browser->>Authenticator: navigator.credentials.create()
    Authenticator->>User: Request biometric/PIN
    User->>Authenticator: Provide verification
    Authenticator->>Authenticator: Generate key pair
    Authenticator->>Authenticator: Store private key securely
    Authenticator->>Browser: Return credential response<br/>(public key + credential ID + attestation data)
    Browser->>Server: Send credential response
    
    alt attestation: "none" (This Demo)
        Note over Server: Skip attestation verification<br/>Accept any authenticator
        Server->>Server: Store public key + credential ID
    else attestation: "indirect"
        Server->>Server: Verify anonymized attestation
        Server->>Server: Confirm authenticator is legitimate
        Server->>Server: Store public key + credential ID
    else attestation: "direct"
        Server->>Server: Verify full attestation statement
        Server->>Server: Check authenticator against policy<br/>(allowlist/blocklist)
        Server->>Server: Store public key + credential ID + device info
    else attestation: "enterprise"
        Server->>Server: Verify enterprise attestation
        Server->>Server: Validate device against MDM policy
        Server->>Server: Store public key + credential ID + unique device ID
    end
    
    Server->>Browser: Registration success
    Browser->>User: Show success message
Loading

Registration Steps

  1. Server generates challenge: Creates random bytes (typically 32 bytes) to prevent replay attacks
  2. Client calls navigator.credentials.create(): Passes challenge, RP info, and user info
  3. Authenticator creates key pair: Private key stored in secure hardware (TPM, Secure Enclave), never leaves device
  4. Client returns credential: Contains public key, credential ID, and attestation data (if requested)
  5. Server verifies and stores:
    • Verifies challenge matches the one sent
    • Verifies origin matches expected RP origin
    • Verifies RP ID matches
    • Checks user verification flag (if required)
    • Verifies attestation (if not "none")
    • Stores public key and credential ID associated with user

Registration Request Example

const publicKeyCredentialCreationOptions = {
    challenge: Uint8Array.from(randomStringFromServer, c => c.charCodeAt(0)),
    rp: {
        name: "Example Corp",
        id: "example.com"
    },
    user: {
        id: Uint8Array.from("UZSL85T9AFC", c => c.charCodeAt(0)),
        name: "user@example.com",
        displayName: "John Doe"
    },
    pubKeyCredParams: [{alg: -7, type: "public-key"}],
    authenticatorSelection: {
        authenticatorAttachment: "platform",
        userVerification: "required"
    },
    timeout: 60000,
    attestation: "none"
};

const credential = await navigator.credentials.create({
    publicKey: publicKeyCredentialCreationOptions
});

Authentication Flow

The authentication ceremony verifies a user with an existing credential.

sequenceDiagram
    participant User
    participant Browser
    participant Server
    participant Authenticator

    User->>Browser: Click "Sign In"
    Browser->>Server: Request authentication options
    Server->>Server: Generate random challenge
    Server->>Browser: Return challenge + allowed credentials
    Browser->>Authenticator: navigator.credentials.get()
    Authenticator->>User: Request biometric/PIN
    User->>Authenticator: Provide verification
    Authenticator->>Authenticator: Retrieve private key
    Authenticator->>Authenticator: Sign challenge
    Authenticator->>Browser: Return assertion (signed challenge + credential ID)
    Browser->>Server: Send authentication response
    Server->>Server: Lookup public key by credential ID
    Server->>Server: Verify signature with public key
    Server->>Server: Create session
    Server->>Browser: Authentication success + session token
    Browser->>User: Redirect to dashboard
Loading

Authentication Steps

  1. Server generates challenge: New random challenge for this authentication attempt
  2. Client calls navigator.credentials.get(): Passes challenge and list of allowed credential IDs
  3. Authenticator signs challenge: Uses stored private key to create digital signature
  4. Client returns assertion: Contains signed challenge, credential ID, and authenticator data
  5. Server verifies signature:
    • Looks up public key by credential ID
    • Verifies challenge matches
    • Verifies origin and RP ID
    • Verifies signature using stored public key
    • Checks signature counter to detect cloned authenticators
    • Creates authenticated session if valid

Authentication Request Example

const publicKeyCredentialRequestOptions = {
    challenge: Uint8Array.from(randomStringFromServer, c => c.charCodeAt(0)),
    allowCredentials: [{
        id: Uint8Array.from(credentialId, c => c.charCodeAt(0)),
        type: 'public-key',
        transports: ['usb', 'ble', 'nfc', 'internal']
    }],
    timeout: 60000,
    userVerification: "required"
};

const assertion = await navigator.credentials.get({
    publicKey: publicKeyCredentialRequestOptions
});

Key Security Features

1. Phishing-Resistant

  • Origin binding: Credentials are cryptographically bound to the domain
  • No credential reuse: Each site gets unique credentials
  • Browser enforced: Cannot be bypassed by malicious scripts

2. No Shared Secrets

  • Private keys never leave device: Stored in secure hardware (TPM, Secure Enclave)
  • Public key cryptography: Server only stores public keys
  • No password databases to breach: Even if server is compromised, attackers can't impersonate users

3. User Verification

  • Built-in 2FA: Combines possession (device) + verification (biometric/PIN)
  • Biometric authentication: Touch ID, Face ID, Windows Hello
  • PIN fallback: When biometrics unavailable

4. Attestation

🔒 This demo uses attestation level: "none" for maximum user privacy

Attestation allows the Relying Party (server) to verify the authenticity and type of the authenticator being used during registration. Different attestation levels provide different trade-offs between privacy and security.

Attestation Levels Comparison

Attestation Level Privacy Security When to Use What Information is Provided
none ✅ Highest ⚠️ Basic Consumer apps, public services (like this demo) No attestation data. Server cannot verify authenticator type or manufacturer. Maximum user privacy.
indirect ✅ High ✅ Medium Privacy-conscious enterprise Anonymized attestation. Server can verify authenticator is legitimate but cannot track individual devices.
direct ⚠️ Low ✅ Highest High-security enterprise, regulated industries Full attestation statement with manufacturer info. Server can verify exact authenticator model and apply device policies.
enterprise ❌ Lowest ✅ Maximum Corporate environments with MDM Full attestation + uniquely identifying information. Allows tracking specific devices for compliance.

Detailed Comparison

none (Used in this demo)

  • Privacy: No tracking of device types or manufacturers
  • Compatibility: Works with all FIDO2-compliant authenticators
  • User experience: No additional prompts or delays
  • ⚠️ Security: Cannot enforce device policies or block specific authenticators
  • 📱 Best for: Consumer applications, public websites, privacy-first services

indirect

  • Privacy: Attestation is anonymized (e.g., via Anonymization CA)
  • Security: Can verify authenticator is legitimate FIDO2 device
  • ⚠️ Tracking: Cannot track individual devices, only verify authenticity
  • 📱 Best for: Enterprise apps that need some verification but respect privacy

direct

  • ⚠️ Privacy: Reveals authenticator make, model, and batch
  • Security: Can enforce allowlist/blocklist of specific authenticator models
  • Compliance: Meets regulatory requirements for device verification
  • 📱 Best for: Banking, healthcare, government services

enterprise

  • Privacy: Can uniquely identify specific devices
  • Security: Full device attestation with unique identifiers
  • Management: Integrates with Mobile Device Management (MDM)
  • 📱 Best for: Corporate internal systems with managed devices

What is MDM (Mobile Device Management)?

MDM stands for Mobile Device Management - security software used by IT departments to monitor, manage, and secure employees' devices (smartphones, tablets, laptops) deployed across the organization.

Key MDM Functions:

  • Device Enrollment: Automatically configure devices with company policies, apps, and certificates
  • Security & Compliance: Enforce password requirements, encryption, remote wipe capabilities
  • Device Tracking: Maintain inventory and uniquely identify specific devices
  • Policy Enforcement: Control device features, restrict apps, enforce updates

MDM in WebAuthn enterprise Attestation:

When using attestation: "enterprise", the authenticator provides unique device identifiers that the server can verify against the company's MDM system:

  • ✅ Only allow passkeys from company-managed devices
  • ✅ Block registration from personal/unmanaged devices
  • ✅ Track which specific device registered each credential
  • ✅ Revoke access if device is removed from MDM

Popular MDM Solutions:

  • Microsoft Intune (Windows, iOS, Android)
  • Jamf (Apple devices)
  • VMware Workspace ONE
  • Google Workspace
  • IBM MaaS360

Privacy Trade-off: MDM + enterprise attestation provides maximum security and control but minimum privacy - every device can be uniquely tracked. This is why consumer apps (like this demo) use attestation: "none" instead.

5. Replay Attack Prevention

  • Challenge-response: Each authentication uses unique random challenge
  • Signature counter: Detects cloned authenticators
  • Time-bound: Challenges expire after timeout

System Architecture

graph TB
    subgraph "Client Side"
        A[Web Browser]
        B[WebAuthn API]
        C[Platform Authenticator<br/>Touch ID, Face ID, Windows Hello]
        D[Roaming Authenticator<br/>USB Security Key]
    end
    
    subgraph "Server Side"
        E[Web Server]
        F[WebAuthn Library]
        G[Database]
    end
    
    A --> B
    B --> C
    B --> D
    A <--> E
    E --> F
    F --> G
    
    G -.->|Stores| H[User Info<br/>Public Keys<br/>Credential IDs<br/>Sign Count]
Loading

Data Flow Overview

flowchart LR
    A[User Action] --> B{Registration or<br/>Authentication?}
    B -->|Registration| C[Create Credential]
    B -->|Authentication| D[Get Credential]
    
    C --> E[Generate Key Pair]
    E --> F[Store Private Key<br/>in Device]
    F --> G[Send Public Key<br/>to Server]
    G --> H[Server Stores<br/>Public Key]
    
    D --> I[Sign Challenge<br/>with Private Key]
    I --> J[Send Signature<br/>to Server]
    J --> K[Server Verifies<br/>with Public Key]
    K --> L{Valid?}
    L -->|Yes| M[Grant Access]
    L -->|No| N[Deny Access]
Loading

Implementation Guide

For Go Projects

  1. Add WebAuthn library:
go get github.com/go-webauthn/webauthn
  1. Initialize WebAuthn:
import "github.com/go-webauthn/webauthn/webauthn"

wconfig := &webauthn.Config{
    RPDisplayName: "Example Corp",
    RPID:          "example.com",
    RPOrigins:     []string{"https://example.com"},
}

webAuthn, err := webauthn.New(wconfig)
  1. Implement User Model:
type User struct {
    ID          []byte
    Name        string
    DisplayName string
    Credentials []webauthn.Credential
}

func (u User) WebAuthnID() []byte {
    return u.ID
}

func (u User) WebAuthnName() string {
    return u.Name
}

func (u User) WebAuthnDisplayName() string {
    return u.DisplayName
}

func (u User) WebAuthnCredentials() []webauthn.Credential {
    return u.Credentials
}

func (u User) WebAuthnIcon() string {
    return ""
}

func (u *User) AddCredential(cred webauthn.Credential) {
    u.Credentials = append(u.Credentials, cred)
}

func (u *User) UpdateCredential(cred webauthn.Credential) {
    for i, c := range u.Credentials {
        if string(c.ID) == string(cred.ID) {
            u.Credentials[i] = cred
            return
        }
    }
}
  1. Create Registration Endpoint:
func BeginRegistration(w http.ResponseWriter, r *http.Request) {
    // Parse request
    var req struct {
        Username    string `json:"username"`
        DisplayName string `json:"display_name"`
    }
    json.NewDecoder(r.Body).Decode(&req)
    
    // Get or create user
    user, err := userStore.GetUser(req.Username)
    if err == ErrUserNotFound {
        user, err = userStore.CreateUser(req.Username, req.DisplayName)
    }
    
    // Generate registration options
    options, sessionData, err := webAuthn.BeginRegistration(user)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    
    // Store session temporarily
    sessionID, _ := sessionStore.SaveSession(sessionData)
    
    // Save session ID in cookie
    session, _ := cookieStore.Get(r, "webauthn-session")
    session.Values["webauthn_session_id"] = sessionID
    session.Values["user_id"] = req.Username
    session.Save(r, w)
    
    json.NewEncoder(w).Encode(options)
}

func FinishRegistration(w http.ResponseWriter, r *http.Request) {
    // Get session
    session, _ := cookieStore.Get(r, "webauthn-session")
    sessionID := session.Values["webauthn_session_id"].(string)
    username := session.Values["user_id"].(string)
    
    // Get user and session data
    user, _ := userStore.GetUser(username)
    sessionData, _ := sessionStore.GetSession(sessionID)
    
    // Finish registration
    credential, err := webAuthn.FinishRegistration(user, *sessionData, r)
    if err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }
    
    // Save credential to user
    user.AddCredential(*credential)
    userStore.SaveUser(user)
    
    // Clean up session
    sessionStore.DeleteSession(sessionID)
    
    w.WriteHeader(http.StatusOK)
}
  1. Create Authentication Endpoint:
func BeginLogin(w http.ResponseWriter, r *http.Request) {
    // Parse request
    var req struct {
        Username string `json:"username"`
    }
    json.NewDecoder(r.Body).Decode(&req)
    
    // Get user
    user, err := userStore.GetUser(req.Username)
    if err != nil {
        http.Error(w, "User not found", http.StatusNotFound)
        return
    }
    
    // Generate login options
    options, sessionData, err := webAuthn.BeginLogin(user)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    
    // Store session
    sessionID, _ := sessionStore.SaveSession(sessionData)
    
    // Save session ID in cookie
    session, _ := cookieStore.Get(r, "webauthn-session")
    session.Values["webauthn_session_id"] = sessionID
    session.Values["user_id"] = req.Username
    session.Save(r, w)
    
    json.NewEncoder(w).Encode(options)
}

func FinishLogin(w http.ResponseWriter, r *http.Request) {
    // Get session
    session, _ := cookieStore.Get(r, "webauthn-session")
    sessionID := session.Values["webauthn_session_id"].(string)
    username := session.Values["user_id"].(string)
    
    // Get user and session data
    user, _ := userStore.GetUser(username)
    sessionData, _ := sessionStore.GetSession(sessionID)
    
    // Finish login
    credential, err := webAuthn.FinishLogin(user, *sessionData, r)
    if err != nil {
        http.Error(w, err.Error(), http.StatusUnauthorized)
        return
    }
    
    // Update credential (sign count, etc.)
    user.UpdateCredential(*credential)
    userStore.SaveUser(user)
    
    // Clean up WebAuthn session
    sessionStore.DeleteSession(sessionID)
    
    // Create authenticated session
    delete(session.Values, "webauthn_session_id")
    session.Values["authenticated"] = true
    session.Save(r, w)
    
    w.WriteHeader(http.StatusOK)
}

API Reference

Registration Endpoints

POST /api/register/begin

Initiates the registration process for a new user or adds a credential to an existing user.

Request Body:

{
  "username": "user@example.com",
  "display_name": "John Doe"
}

Response: PublicKeyCredentialCreationOptions (WebAuthn standard format)

Status Codes:

  • 200 OK: Registration options generated successfully
  • 400 Bad Request: Invalid request body or missing username
  • 500 Internal Server Error: Failed to generate options

POST /api/register/finish

Completes the registration process by verifying the credential.

Request Body: PublicKeyCredential with attestation response (WebAuthn standard format)

Response:

{
  "status": "success",
  "message": "Registration completed successfully"
}

Status Codes:

  • 200 OK: Registration completed successfully
  • 400 Bad Request: Invalid credential or verification failed
  • 401 Unauthorized: Invalid session
  • 500 Internal Server Error: Failed to save credential

Authentication Endpoints

POST /api/login/begin

Initiates the authentication process for an existing user.

Request Body:

{
  "username": "user@example.com"
}

Response: PublicKeyCredentialRequestOptions (WebAuthn standard format)

Status Codes:

  • 200 OK: Login options generated successfully
  • 400 Bad Request: Invalid request or user has no credentials
  • 404 Not Found: User not found
  • 500 Internal Server Error: Failed to generate options

POST /api/login/finish

Completes the authentication process by verifying the assertion.

Request Body: PublicKeyCredential with assertion response (WebAuthn standard format)

Response:

{
  "status": "success",
  "message": "Login completed successfully",
  "user": {
    "username": "user@example.com",
    "display_name": "John Doe"
  }
}

Status Codes:

  • 200 OK: Login completed successfully
  • 400 Bad Request: Session not found
  • 401 Unauthorized: Invalid credential or verification failed
  • 500 Internal Server Error: Failed to update credential

User Endpoints

GET /api/user

Returns information about the currently authenticated user.

Response:

{
  "username": "user@example.com",
  "display_name": "John Doe",
  "credentials": 2
}

Status Codes:

  • 200 OK: User information returned
  • 401 Unauthorized: Not authenticated
  • 404 Not Found: User not found

POST /api/logout

Logs out the current user by clearing the session.

Response:

{
  "status": "success",
  "message": "Logged out successfully"
}

Status Codes:

  • 200 OK: Logout successful
  • 401 Unauthorized: Invalid session
  • 500 Internal Server Error: Failed to clear session

Configuration

Environment Variables

Configure the server using these environment variables:

Variable Description Default Required
RP_DISPLAY_NAME Display name for your application shown to users "WebAuthn Demo" No
RP_ID Relying Party ID - must match your domain (without protocol/port) "localhost" No
RP_ORIGIN Full origin URL including protocol and port "http://localhost:8080" No
PORT Server port to listen on "8080" No

Example Configuration

Development (localhost):

export RP_DISPLAY_NAME="My WebAuthn App"
export RP_ID="localhost"
export RP_ORIGIN="http://localhost:8080"
export PORT="8080"

Production:

export RP_DISPLAY_NAME="My WebAuthn App"
export RP_ID="example.com"
export RP_ORIGIN="https://example.com"
export PORT="443"

Important Notes

  • RP_ID must match the domain where your app is hosted (e.g., example.com or subdomain.example.com)
  • RP_ORIGIN must include the full origin with protocol (e.g., https://example.com)
  • HTTPS is required in production - WebAuthn only works over HTTPS (except for localhost)
  • Credentials are bound to the RP_ID and cannot be used across different domains

Browser Support

WebAuthn is supported in all modern browsers:

  • ✅ Chrome 67+
  • ✅ Firefox 60+
  • ✅ Safari 13+
  • ✅ Edge 18+
  • ✅ Opera 54+

Troubleshooting

Common Issues

1. "NotAllowedError: The operation either timed out or was not allowed"

Causes:

  • User cancelled the authentication prompt
  • Timeout expired (default 60 seconds)
  • User gesture required but not present

Solutions:

  • Ensure the WebAuthn call is triggered by a user action (button click)
  • Increase timeout if needed
  • Check browser console for specific error details

2. "SecurityError: The operation is insecure"

Causes:

  • Not using HTTPS in production
  • RP_ID doesn't match the domain
  • Origin mismatch

Solutions:

  • Use HTTPS in production (localhost is exempt)
  • Verify RP_ID matches your domain exactly
  • Check RP_ORIGIN includes correct protocol and domain

3. "NotSupportedError: The authenticator does not support this operation"

Causes:

  • Authenticator doesn't support the requested algorithm
  • Platform authenticator not available
  • User verification not supported

Solutions:

  • Include multiple algorithms in pubKeyCredParams (e.g., ES256, RS256)
  • Set authenticatorAttachment to undefined to allow any authenticator
  • Make userVerification optional: "preferred" instead of "required"

4. "InvalidStateError: The user is already registered"

Causes:

  • Credential already exists for this user
  • excludeCredentials list contains existing credential

Solutions:

  • This is expected behavior - prevents duplicate registrations
  • Allow user to add additional credentials instead
  • Clear existing credentials if re-registration is needed

5. "User not found" or "No credentials registered"

Causes:

  • User hasn't registered yet
  • Username mismatch
  • Database/storage issue

Solutions:

  • Verify user completed registration successfully
  • Check username spelling and case sensitivity
  • Verify credentials are being saved to storage

6. Session or Cookie Issues

Causes:

  • Cookies blocked by browser
  • Session expired
  • SameSite cookie policy

Solutions:

  • Check browser cookie settings
  • Verify SameSite attribute is set correctly
  • Use shorter session timeouts for WebAuthn sessions

Debugging Tips

  1. Enable verbose logging: Check browser console and server logs
  2. Test with different authenticators: Try platform (Touch ID) vs roaming (YubiKey)
  3. Verify RP configuration: Double-check RP_ID and RP_ORIGIN match your deployment
  4. Test in incognito mode: Rules out extension interference
  5. Check WebAuthn support: Visit webauthn.io to test browser support

Security Considerations

Production Deployment

1. HTTPS is Mandatory

  • WebAuthn requires HTTPS in production (localhost is exempt for testing)
  • Use valid SSL/TLS certificates
  • Enable HSTS (HTTP Strict Transport Security)

2. Secure Session Management

  • Use secure, HTTP-only cookies
  • Set appropriate SameSite attribute (Strict or Lax)
  • Implement session expiration and rotation
  • Use cryptographically random session keys (32+ bytes)
cookieStore.Options = &sessions.Options{
    Path:     "/",
    MaxAge:   86400 * 7, // 7 days
    HttpOnly: true,
    Secure:   true,  // MUST be true in production
    SameSite: http.SameSiteStrictMode,
}

3. Challenge Security

  • Generate cryptographically random challenges (32+ bytes)
  • Challenges must be single-use only
  • Implement challenge expiration (60 seconds recommended)
  • Never reuse challenges across sessions

4. Origin and RP ID Validation

  • Server must validate origin matches expected value
  • RP_ID must match your domain exactly
  • Reject requests from unexpected origins
  • The go-webauthn library handles this automatically

5. Credential Storage

  • Store public keys securely in database
  • Associate credentials with user accounts properly
  • Track signature counter to detect cloned authenticators
  • Never store private keys (they never leave the authenticator)

6. User Verification

  • Require user verification for sensitive operations
  • Set userVerification: "required" for high-security scenarios
  • Understand the difference between user verification and user presence

7. Rate Limiting

  • Implement rate limiting on registration/login endpoints
  • Prevent brute force attacks
  • Monitor for suspicious patterns

8. Attestation Considerations

  • This demo uses attestation: "none" for privacy
  • Consider "direct" attestation for high-security enterprise use
  • Validate attestation statements if using direct/indirect attestation
  • Maintain allowlist/blocklist of authenticator models if needed

Best Practices

  1. Implement account recovery: Provide backup authentication methods
  2. Allow multiple credentials: Let users register multiple authenticators
  3. Inform users: Explain what passkeys are and how they work
  4. Test thoroughly: Test with different browsers and authenticator types
  5. Monitor and log: Track authentication attempts and failures
  6. Keep dependencies updated: Regularly update the go-webauthn library
  7. Implement CSRF protection: Use CSRF tokens for state-changing operations
  8. Validate all inputs: Never trust client-side data

Privacy Considerations

  • Using attestation: "none" prevents tracking users across sites
  • Don't log or store personally identifiable information unnecessarily
  • Inform users about data collection in privacy policy
  • Consider GDPR/privacy regulations in your jurisdiction

Resources

About

A comprehensive guide to understanding and implementing FIDO Passkey WebAuthn authentication.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published