# Low Level Design: Passkey Authentication

Source: https://www.techinterview.org/post/3233470866/lld-passkey-auth/
Updated: 2026-06-30 · techinterview.org

## WebAuthn / FIDO2 Overview

Passkeys use public-key cryptography. The private key never leaves the device; the server stores only the public key.

## Registration Flow

  - Server generates a random challenge and returns it along with `rp` (relying party) info.

  - Browser calls `navigator.credentials.create()` with the challenge.

  - Authenticator generates an asymmetric key pair; private key stored in secure enclave.

  - Authenticator returns an attestation object containing the public key, credential ID, and signed challenge.

  - Server validates attestation and stores the credential.


```
// Client-side (simplified)
const credential = await navigator.credentials.create({
  publicKey: {
    challenge: base64ToArrayBuffer(serverChallenge),
    rp: { name: "Example", id: "example.com" },
    user: { id: userId, name: userEmail, displayName: userName },
    pubKeyCredParams: [{ type: "public-key", alg: -7 }] // ES256
  }
});
// POST credential.response to /webauthn/register
```


## Authentication (Assertion) Flow

  - Server generates a fresh challenge and returns allowed credential IDs for the user.

  - Browser calls `navigator.credentials.get()`.

  - Authenticator signs the challenge with the stored private key.

  - Server verifies the signature against the stored public key and checks the sign count.


```
// Client-side (simplified)
const assertion = await navigator.credentials.get({
  publicKey: {
    challenge: base64ToArrayBuffer(serverChallenge),
    allowCredentials: [{ type: "public-key", id: credentialId }]
  }
});
// POST assertion.response to /webauthn/authenticate
```


## Data Model

### Credential Table


```
Credential (
  id              BIGSERIAL PRIMARY KEY,
  user_id         BIGINT,
  credential_id   BYTEA UNIQUE,        -- opaque handle from authenticator
  public_key      BYTEA,               -- COSE-encoded public key
  aaguid          UUID,                -- authenticator model identifier
  sign_count      INT DEFAULT 0,       -- replay protection
  device_name     TEXT,                -- user-provided label
  created_at      TIMESTAMP,
  last_used_at    TIMESTAMP
)
```


### Challenge Store (Redis)


```
SET challenge:{session_id} <base64_challenge> EX 300   -- 5-minute TTL
```


## Registration Validation

  - Verify attestation statement format and signature.

  - Check `origin` matches expected RP origin.

  - Verify `rpIdHash` in authenticatorData matches SHA-256 of the RP ID.

  - Confirm the challenge in clientDataJSON matches the stored challenge.

## Assertion Validation

  - Re-verify rpIdHash and origin.

  - Verify signature over `authenticatorData || SHA-256(clientDataJSON)` using stored public key.

  - Assert `sign_count > stored_sign_count` to detect cloned authenticators; update stored value.

## Multiple Passkeys and Sync

Users may register multiple credentials (phone + laptop). Synced passkeys are backed up via iCloud Keychain or Google Password Manager, identified by `aaguid`. Platform authenticators set the `BS` (backup state) flag in authenticatorData.

## Fallback Flow

Users without a registered passkey fall back to password + 2FA. The registration prompt is shown post-login to encourage passkey adoption.

See also: [Netflix Interview Guide 2026: Streaming Architecture, Recommendation Systems, and Engineering Excellence](https://www.techinterview.org/post/3233460274/netflix-interview-guide-2026-streaming-architecture-recommendation-systems-and-engineering-excellence/)

See also: [Apple Interview Guide 2026: iOS Systems, Hardware-Software Integration, and iCloud Architecture](https://www.techinterview.org/post/3233460277/apple-interview-guide-2026-ios-systems-hardware-software-integration-and-icloud-architecture/)

See also: [Meta Interview Guide 2026: Facebook, Instagram, WhatsApp Engineering](https://www.techinterview.org/post/3233460272/meta-interview-guide-2026-facebook-instagram-whatsapp-engineering/)
