-- Passkeys (WebAuthn discoverable credentials): an alternative to the email -- magic link. A user may register several (one per device/authenticator). CREATE TABLE IF NOT EXISTS passkeys ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL REFERENCES users (id) ON DELETE CASCADE, credential_id TEXT NOT NULL UNIQUE, public_key TEXT NOT NULL, sign_count INTEGER NOT NULL DEFAULT 0, label TEXT NOT NULL DEFAULT '', created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')), last_used_at TEXT NULL ); CREATE INDEX IF NOT EXISTS idx_passkeys_user ON passkeys (user_id); -- Short-lived, single-use WebAuthn challenges bridging the "options" and -- "verify" calls of both the registration and login ceremonies. user_id is -- set for a registration (tied to the signed-in caller) and NULL for a login -- attempt, since who's logging in isn't known until the credential comes back. CREATE TABLE IF NOT EXISTS webauthn_challenges ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NULL REFERENCES users (id) ON DELETE CASCADE, purpose TEXT NOT NULL CHECK (purpose IN ('register', 'login')), challenge TEXT NOT NULL, expires_at TEXT NOT NULL, consumed_at TEXT NULL, created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')) ); CREATE INDEX IF NOT EXISTS idx_webauthn_challenges_expiry ON webauthn_challenges (expires_at);