-- Discoverable WebAuthn credentials used for passwordless sign-in. CREATE TABLE passkey_credentials ( id SERIAL PRIMARY KEY, user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, credential_id VARCHAR(512) UNIQUE NOT NULL, public_key BYTEA NOT NULL, counter BIGINT NOT NULL DEFAULT 0, transports TEXT[] NOT NULL DEFAULT '{}', device_type VARCHAR(32) NOT NULL, backed_up BOOLEAN NOT NULL DEFAULT false, label VARCHAR(100), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, last_used_at TIMESTAMP ); CREATE INDEX idx_passkey_credentials_user_id ON passkey_credentials(user_id); -- Challenges are short-lived and single-use. Keeping them in PostgreSQL lets -- verification work across backend restarts and multiple application instances. CREATE TABLE passkey_challenges ( id SERIAL PRIMARY KEY, user_id INTEGER REFERENCES users(id) ON DELETE CASCADE, purpose VARCHAR(32) NOT NULL CHECK (purpose IN ('registration', 'authentication')), challenge VARCHAR(512) NOT NULL, expires_at TIMESTAMP NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE(purpose, challenge) ); CREATE INDEX idx_passkey_challenges_lookup ON passkey_challenges(purpose, challenge, expires_at);