Skip to main content
Passkeys provide a secure, passwordless authentication method using WebAuthn. Users can sign in with biometrics, PINs, or security keys instead of passwords, offering both better security and user experience.

How Passkey Authentication Works

Guile supports two passkey authentication flows through a unified challenge API: The most seamless experience where users authenticate without entering any identifier: Key benefits:
  • No username or email required
  • Faster sign-in experience
  • Works across all user’s devices with passkeys

Username-First Authentication

Traditional flow where users provide their identifier first: When to use:
  • User prefers to enter their identifier
  • Fallback when usernameless fails
  • Backward compatibility with existing flows

Registering a Passkey

Before users can authenticate with passkeys, they must register at least one:
  1. Get registration options - Call POST /auth/passkeyRegistrationOptions to obtain WebAuthn credential creation options
  2. Create credential - Use the WebAuthn API to create a discoverable credential
  3. Register passkey - Call POST /auth/passkeys to send the credential to the server
The registration process creates a discoverable credential (resident key) that:
  • Stores user information on the authenticator device
  • Enables usernameless authentication
  • Works for both authentication flows

Registration Options Structure

The API returns human-readable field names for easy integration:
  • relyingParty - Information about your application (not abbreviated as “rp”)
  • publicKeyCredentialParameters - Supported algorithms (not abbreviated as “pubKeyCredParams”)
  • algorithm - Cryptographic algorithm enum values like ES256, RS256 (not numeric codes)

Supported Algorithms

The API supports these cryptographic algorithms (COSE identifiers):
  • ES256 (recommended) - ECDSA with SHA-256, widely supported
  • RS256 - RSASSA-PKCS1-v1_5 with SHA-256
  • EdDSA - EdDSA (Ed25519)
  • ES384 - ECDSA with SHA-384
  • ES512 - ECDSA with SHA-512
Your client SDK should map these human-readable values to the appropriate COSE numeric identifiers when calling the WebAuthn API.

Discoverable Credentials

All passkeys in Guile are created as discoverable credentials with these characteristics:
  • Resident keys: Stored on the authenticator device itself
  • User information included: Contains user identifier for server lookup
  • Browser discoverable: Can be found without server input
  • Cross-device sync: Syncs across user’s devices (platform-dependent)
The registration options automatically configure:
  • authenticatorSelection.residentKey: "required"
  • authenticatorSelection.requireResidentKey: true
This ensures all passkeys support usernameless authentication while remaining compatible with username-first flows.

Unified Challenge API

Passkey authentication uses the same unified challenge endpoints as OTP authentication:

Challenge Creation: POST /auth/challenges

Request body:
{
  "method": "passkey",
  "phoneNumber": "+1234567890"  // Optional: omit for usernameless
}
Response contains WebAuthn options with human-readable field names:
{
  "challenge": "base64url-encoded-challenge",
  "relyingPartyId": "guile.app",
  "allowCredentials": [],  // Empty for usernameless
  "userVerification": "preferred"
}

Verification: POST /auth/verifiedChallenges

Request body:
{
  "method": "passkey",
  "assertion": "JSON-serialized-WebAuthn-assertion"
}
Response contains access and refresh tokens (same as OTP authentication).

Authentication Flow Details

Usernameless Flow

  1. Call POST /auth/challenges with method: "passkey" (no identifier)
  2. The API returns WebAuthn options with an empty allowCredentials array
  3. The browser discovers available passkeys from the device’s keychain
  4. User selects a passkey and verifies their identity
  5. Call POST /auth/verifiedChallenges with method: "passkey" and the assertion
  6. The server extracts the credential ID, identifies the user, and verifies the assertion
  7. Receive access and refresh tokens

Username-First Flow

  1. User provides their email or phone number
  2. Call POST /auth/challenges with method: "passkey" and the identifier (phoneNumber or emailAddress)
  3. The API returns WebAuthn options with the user’s passkeys in allowCredentials
  4. The browser shows only that user’s passkeys
  5. User selects a passkey and verifies their identity
  6. Call POST /auth/verifiedChallenges with method: "passkey" and the assertion
  7. Receive access and refresh tokens

Security Considerations

Passkeys provide strong security guarantees:
  • Phishing resistant: Credentials are bound to the domain
  • No shared secrets: Private keys never leave the device
  • Biometric verification: User presence and verification required
  • Replay protection: Each assertion includes a unique challenge

Managing Passkeys

Users can manage their registered passkeys:
  • List passkeys: View all registered passkeys with device names and last used timestamps
  • Delete passkeys: Remove passkeys from lost or replaced devices
  • Multiple passkeys: Register passkeys on multiple devices for convenience

Best Practices

  1. Offer both flows: Support usernameless for convenience and username-first as fallback
  2. Clear labeling: Use “Sign in with passkey” for usernameless, “Use passkey” for username-first
  3. Device naming: Encourage users to name passkeys descriptively (e.g., “iPhone 15 Pro”)
  4. Graceful fallback: Provide OTP authentication as backup using the same unified endpoints
  5. User education: Explain passkey benefits during registration
  6. Field mapping: Your client SDK should map human-readable field names (like relyingParty, algorithm) to WebAuthn specification names
  7. Algorithm support: Use ES256 as the primary algorithm for maximum compatibility

Browser Support

Passkeys are supported in modern browsers:
  • Chrome/Edge 108+
  • Safari 16+
  • Firefox 119+
Always check for WebAuthn support before offering passkey authentication.