Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

Passkeys

Vault uses passkeys (WebAuthn) for authentication, providing phishing-resistant, passwordless login.

What Are Passkeys?

Passkeys are cryptographic credentials that replace passwords:

  • Phishing-resistant: Bound to specific domains
  • No shared secrets: Public key cryptography
  • Biometric: Unlock with fingerprint or face
  • Cross-device: Sync across your devices
┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│   Your Device   │     │   Authenticator │     │   Vault Server  │
│                 │     │   (TPM/Secure   │     │                 │
│  Browser/App    │◀───▶│    Enclave)     │◀───▶│  Stores Public  │
│                 │     │                 │     │  Key Only       │
│  Triggers Auth  │     │  Signs Challenge│     │  Verifies Sig   │
└─────────────────┘     └─────────────────┘     └─────────────────┘

Why Passkeys?

vs. Passwords

FeaturePasswordsPasskeys
PhishingVulnerableResistant
Reuse attacksVulnerableImmune
Brute forceVulnerableImmune
Data breachesExposedOnly public key
User frictionHighLow

vs. 2FA

FeatureTOTP/SMSPasskeys
PhishingVulnerableResistant
SIM swapVulnerable (SMS)Immune
Code entryRequiredNot needed
Single stepNoYes

How It Works

Registration

  1. Server generates random challenge
  2. Authenticator creates key pair
  3. Private key stored in secure hardware
  4. Public key sent to server
// Server generates options
const options = await generateRegistrationOptions({
  rpName: "Vault",
  rpID: "vault.oxc.sh",
  userID: userId,
  userName: email,
  authenticatorSelection: {
    residentKey: "required",
    userVerification: "required"
  }
});
 
// Client creates credential
const credential = await navigator.credentials.create({
  publicKey: options
});
 
// Server stores public key
await storeCredential(userId, credential);

Authentication

  1. Server generates random challenge
  2. Authenticator signs challenge with private key
  3. User verifies with biometric
  4. Server verifies signature with public key
// Server generates options
const options = await generateAuthenticationOptions({
  rpID: "vault.oxc.sh",
  allowCredentials: userCredentials,
  userVerification: "required"
});
 
// Client signs challenge
const assertion = await navigator.credentials.get({
  publicKey: options
});
 
// Server verifies signature
const verified = await verifyAuthenticationResponse({
  response: assertion,
  expectedChallenge: challenge,
  expectedOrigin: "https://vault.oxc.sh",
  expectedRPID: "vault.oxc.sh",
  credential: storedCredential
});

Phishing Resistance

Passkeys are bound to the relying party (RP) origin:

✅ https://vault.oxc.sh      → Passkey works
❌ https://vault.oxc.sh.fake → Passkey refuses to sign
❌ https://voult.oxc.sh      → Passkey refuses to sign

The authenticator checks:

  1. Origin: Must match registered domain
  2. RP ID: Must match registered RP ID
  3. TLS: Must be HTTPS

Even if you're tricked, your passkey won't authenticate to a fake site.

User Verification

Vault requires user verification for every authentication:

MethodPlatform
Touch IDmacOS, iOS
Face IDiOS
Windows HelloWindows
FingerprintAndroid
PINAll platforms

This ensures someone with physical access to your device still needs biometric or PIN.

Supported Authenticators

Platform Authenticators (Built-in)

PlatformAuthenticatorSync
AppleiCloud KeychainiCloud
GoogleGoogle Password ManagerGoogle Account
WindowsWindows HelloMicrosoft Account

Roaming Authenticators (External)

DeviceType
YubiKeyUSB/NFC
Titan KeyUSB/NFC
FeitianUSB/NFC

PRF Extension

Vault uses the WebAuthn PRF extension to derive vault encryption keys:

const credential = await navigator.credentials.get({
  publicKey: {
    ...options,
    extensions: {
      prf: {
        eval: {
          first: saltBytes
        }
      }
    }
  }
});
 
// PRF output used to derive vault key
const prfOutput = credential.getClientExtensionResults().prf?.results?.first;
Benefits:
  • Hardware-bound encryption key
  • No master password needed (if PRF supported)
  • Key never leaves secure hardware
Limitations:
  • Not all authenticators support PRF
  • Falls back to master password if unavailable

CLI Authentication

The CLI can't perform WebAuthn directly (no browser context), so it uses browser delegation:

  1. CLI creates auth session on server
  2. CLI opens browser with session ID
  3. User completes passkey auth in browser
  4. Browser completes session
  5. CLI polls and receives token

See CLI Authentication for details.

Security Considerations

Lost Authenticator

If you lose your passkey authenticator:

  1. Platform passkeys sync automatically
  2. Register backup authenticator recommended
  3. Account recovery requires identity verification

Compromised Authenticator

If your authenticator is compromised:

  1. Revoke credential in Vault settings
  2. Re-register with new authenticator
  3. No password to change

Platform Security

Passkey security depends on:

  • Device lock (PIN/biometric)
  • Platform integrity
  • Secure enclave implementation

Browser Support

BrowserPlatform PasskeysRoaming (USB)
Chrome 108+
Safari 16+
Firefox 122+
Edge 108+

Related