Some checks failed
Build and Deploy Docs / build-and-deploy (push) Has been cancelled
- Created logs/ structure (sessions, operations, incidents) - Added session-start/log/end scripts - Installed Git hooks for auto-logging commits/pushes - Added shell integration for zsh - Created CHANGELOG.md - Documented today's session (2026-01-10)
159 lines
5.3 KiB
PL/PgSQL
159 lines
5.3 KiB
PL/PgSQL
-- migrations/006_create_passkey_tables.sql
|
|
-- Passkey (WebAuthn) Authentication Schema
|
|
|
|
-- Enable UUID extension if not already enabled
|
|
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
|
|
|
-- ============================================================================
|
|
-- Users Table (if not exists)
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS users (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
email TEXT UNIQUE,
|
|
username TEXT UNIQUE,
|
|
display_name TEXT,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
last_login_at TIMESTAMPTZ
|
|
);
|
|
|
|
-- ============================================================================
|
|
-- Passkeys Table (WebAuthn Credentials)
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS passkeys (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
user_id UUID 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,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
last_used_at TIMESTAMPTZ,
|
|
device_name TEXT,
|
|
transports TEXT[], -- USB, NFC, BLE, internal
|
|
aaguid TEXT,
|
|
attestation_format TEXT
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_passkeys_user_id ON passkeys(user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_passkeys_credential_id ON passkeys(credential_id);
|
|
CREATE INDEX IF NOT EXISTS idx_passkeys_last_used ON passkeys(last_used_at DESC);
|
|
|
|
-- ============================================================================
|
|
-- Sessions Table
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS sessions (
|
|
token TEXT PRIMARY KEY,
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
expires_at TIMESTAMPTZ NOT NULL,
|
|
last_activity_at TIMESTAMPTZ,
|
|
user_agent TEXT,
|
|
ip_address TEXT
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_sessions_user_id ON sessions(user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_sessions_expires_at ON sessions(expires_at);
|
|
|
|
-- ============================================================================
|
|
-- Passkey Challenges (temporary, for WebAuthn flow)
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS passkey_challenges (
|
|
challenge TEXT PRIMARY KEY,
|
|
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
|
|
email TEXT, -- For registration flow when user doesn't exist yet
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
expires_at TIMESTAMPTZ NOT NULL,
|
|
challenge_type TEXT NOT NULL, -- 'register' or 'authenticate'
|
|
rp_id TEXT NOT NULL,
|
|
origin TEXT NOT NULL
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_passkey_challenges_expires_at ON passkey_challenges(expires_at);
|
|
CREATE INDEX IF NOT EXISTS idx_passkey_challenges_user_id ON passkey_challenges(user_id);
|
|
|
|
-- ============================================================================
|
|
-- User MicroDAO Memberships (for ActorIdentity)
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS user_microdao_memberships (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
microdao_id TEXT NOT NULL,
|
|
role TEXT NOT NULL, -- 'owner', 'admin', 'member'
|
|
joined_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
left_at TIMESTAMPTZ,
|
|
|
|
UNIQUE (user_id, microdao_id)
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_memberships_user_id ON user_microdao_memberships(user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_memberships_microdao_id ON user_microdao_memberships(microdao_id);
|
|
|
|
-- ============================================================================
|
|
-- Cleanup Function (remove expired challenges and sessions)
|
|
-- ============================================================================
|
|
|
|
CREATE OR REPLACE FUNCTION cleanup_expired_auth_data()
|
|
RETURNS void AS $$
|
|
BEGIN
|
|
DELETE FROM passkey_challenges WHERE expires_at < now();
|
|
DELETE FROM sessions WHERE expires_at < now();
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
-- ============================================================================
|
|
-- Sample Data (for testing)
|
|
-- ============================================================================
|
|
|
|
-- Create test user
|
|
INSERT INTO users (id, email, username, display_name)
|
|
VALUES
|
|
('00000000-0000-0000-0000-000000000001', 'admin@daarion.city', 'admin', 'Admin User'),
|
|
('00000000-0000-0000-0000-000000000093', 'user93@daarion.city', 'user93', 'User 93')
|
|
ON CONFLICT (email) DO NOTHING;
|
|
|
|
-- Add microDAO memberships
|
|
INSERT INTO user_microdao_memberships (user_id, microdao_id, role)
|
|
VALUES
|
|
('00000000-0000-0000-0000-000000000001', 'microdao:daarion', 'owner'),
|
|
('00000000-0000-0000-0000-000000000093', 'microdao:7', 'owner'),
|
|
('00000000-0000-0000-0000-000000000093', 'microdao:daarion', 'member')
|
|
ON CONFLICT (user_id, microdao_id) DO NOTHING;
|
|
|
|
-- ============================================================================
|
|
-- Triggers for updated_at
|
|
-- ============================================================================
|
|
|
|
CREATE OR REPLACE FUNCTION update_timestamp()
|
|
RETURNS TRIGGER AS $$
|
|
BEGIN
|
|
NEW.updated_at = now();
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
CREATE TRIGGER set_users_timestamp
|
|
BEFORE UPDATE ON users
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION update_timestamp();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|