import { getDb } from './db'; /** Ein gespeicherter Passkey (WebAuthn-Credential) für den Single-User. */ export interface PasskeyRecord { credentialId: string; // base64url publicKey: Uint8Array; counter: number; transports: string[]; label: string; createdAt: string; lastUsedAt: string | null; } interface PasskeyRow { credential_id: string; public_key: Buffer; counter: number; transports: string; label: string; created_at: string; last_used_at: string | null; } function fromRow(row: PasskeyRow): PasskeyRecord { let transports: string[] = []; try { const parsed = JSON.parse(row.transports); if (Array.isArray(parsed)) transports = parsed; } catch { /* ignore malformed transports */ } return { credentialId: row.credential_id, // Uint8Array.from kopiert in einen frischen ArrayBuffer (passt zum von // @simplewebauthn erwarteten Uint8Array). publicKey: Uint8Array.from(row.public_key), counter: row.counter, transports, label: row.label, createdAt: row.created_at, lastUsedAt: row.last_used_at, }; } /** Alle Passkeys, neueste zuerst. */ export function listPasskeys(): PasskeyRecord[] { return ( getDb().prepare('SELECT * FROM passkeys ORDER BY created_at DESC').all() as PasskeyRow[] ).map(fromRow); } export function getPasskey(credentialId: string): PasskeyRecord | undefined { const row = getDb() .prepare('SELECT * FROM passkeys WHERE credential_id = ?') .get(credentialId) as PasskeyRow | undefined; return row ? fromRow(row) : undefined; } export function insertPasskey(record: { credentialId: string; publicKey: Uint8Array; counter: number; transports: string[]; label: string; }): void { getDb() .prepare( `INSERT INTO passkeys (credential_id, public_key, counter, transports, label) VALUES (@credentialId, @publicKey, @counter, @transports, @label)` ) .run({ credentialId: record.credentialId, publicKey: Buffer.from(record.publicKey), counter: record.counter, transports: JSON.stringify(record.transports ?? []), label: record.label, }); } /** Aktualisiert Counter und last_used_at nach erfolgreicher Anmeldung (Replay-Schutz). */ export function updatePasskeyUsage(credentialId: string, counter: number): void { getDb() .prepare( `UPDATE passkeys SET counter = @counter, last_used_at = datetime('now') WHERE credential_id = @credentialId` ) .run({ credentialId, counter }); } export function deletePasskey(credentialId: string): boolean { return ( getDb().prepare('DELETE FROM passkeys WHERE credential_id = ?').run(credentialId).changes > 0 ); }