-- +up CREATE TABLE IF NOT EXISTS wrn_auth_users ( id VARCHAR(191) PRIMARY KEY, username VARCHAR(191), display_name VARCHAR(191), avatar_url TEXT, status VARCHAR(32) NOT NULL, roles_json TEXT NOT NULL, email_verified INTEGER NOT NULL DEFAULT 0, phone_verified INTEGER NOT NULL DEFAULT 0, mfa_enabled INTEGER NOT NULL DEFAULT 0, locale VARCHAR(32), timezone VARCHAR(64), created_at BIGINT NOT NULL, updated_at BIGINT NOT NULL, last_login_at BIGINT, metadata_json TEXT ); CREATE UNIQUE INDEX IF NOT EXISTS wrn_auth_users_username_uq ON wrn_auth_users(username); CREATE TABLE IF NOT EXISTS wrn_auth_identities ( id VARCHAR(191) PRIMARY KEY, user_id VARCHAR(191) NOT NULL, type VARCHAR(32) NOT NULL, value VARCHAR(320) NOT NULL, normalized_value VARCHAR(320) NOT NULL, is_primary INTEGER NOT NULL DEFAULT 0, verified_at BIGINT, created_at BIGINT NOT NULL, updated_at BIGINT NOT NULL, UNIQUE(type, normalized_value) ); CREATE INDEX IF NOT EXISTS wrn_auth_identities_user_idx ON wrn_auth_identities(user_id); CREATE TABLE IF NOT EXISTS wrn_auth_password_credentials ( user_id VARCHAR(191) PRIMARY KEY, password_hash TEXT NOT NULL, password_version INTEGER NOT NULL DEFAULT 1, changed_at BIGINT NOT NULL, must_change INTEGER NOT NULL DEFAULT 0 ); CREATE TABLE IF NOT EXISTS wrn_auth_sessions ( id VARCHAR(191) PRIMARY KEY, user_id VARCHAR(191) NOT NULL, device_id VARCHAR(191) NOT NULL, created_at BIGINT NOT NULL, last_seen_at BIGINT NOT NULL, expires_at BIGINT NOT NULL, absolute_expires_at BIGINT NOT NULL, ip VARCHAR(64), user_agent TEXT, trusted INTEGER NOT NULL DEFAULT 0, revoked_at BIGINT, revoke_reason VARCHAR(191), metadata_json TEXT ); CREATE INDEX IF NOT EXISTS wrn_auth_sessions_user_idx ON wrn_auth_sessions(user_id); CREATE INDEX IF NOT EXISTS wrn_auth_sessions_expiry_idx ON wrn_auth_sessions(expires_at); CREATE TABLE IF NOT EXISTS wrn_auth_trusted_devices ( id VARCHAR(191) PRIMARY KEY, user_id VARCHAR(191) NOT NULL, name VARCHAR(191) NOT NULL, fingerprint_hash VARCHAR(191) NOT NULL, created_at BIGINT NOT NULL, last_seen_at BIGINT NOT NULL, expires_at BIGINT NOT NULL, revoked_at BIGINT, UNIQUE(user_id, fingerprint_hash) ); CREATE TABLE IF NOT EXISTS wrn_auth_tokens ( id VARCHAR(191) PRIMARY KEY, user_id VARCHAR(191) NOT NULL, purpose VARCHAR(64) NOT NULL, token_hash VARCHAR(191) NOT NULL UNIQUE, target VARCHAR(320), created_at BIGINT NOT NULL, expires_at BIGINT NOT NULL, used_at BIGINT, attempts INTEGER NOT NULL DEFAULT 0, max_attempts INTEGER NOT NULL, metadata_json TEXT ); CREATE INDEX IF NOT EXISTS wrn_auth_tokens_user_idx ON wrn_auth_tokens(user_id); CREATE INDEX IF NOT EXISTS wrn_auth_tokens_expiry_idx ON wrn_auth_tokens(expires_at); CREATE TABLE IF NOT EXISTS wrn_auth_otp_challenges ( id VARCHAR(191) PRIMARY KEY, user_id VARCHAR(191) NOT NULL, method VARCHAR(32) NOT NULL, destination VARCHAR(320) NOT NULL, code_hash VARCHAR(191) NOT NULL, created_at BIGINT NOT NULL, expires_at BIGINT NOT NULL, used_at BIGINT, attempts INTEGER NOT NULL DEFAULT 0, max_attempts INTEGER NOT NULL ); CREATE TABLE IF NOT EXISTS wrn_auth_totp_credentials ( id VARCHAR(191) PRIMARY KEY, user_id VARCHAR(191) NOT NULL, label VARCHAR(191) NOT NULL, secret TEXT NOT NULL, created_at BIGINT NOT NULL, verified_at BIGINT, last_counter BIGINT ); CREATE TABLE IF NOT EXISTS wrn_auth_recovery_codes ( id VARCHAR(191) PRIMARY KEY, user_id VARCHAR(191) NOT NULL, code_hash VARCHAR(191) NOT NULL, created_at BIGINT NOT NULL, used_at BIGINT ); CREATE INDEX IF NOT EXISTS wrn_auth_recovery_user_idx ON wrn_auth_recovery_codes(user_id); CREATE TABLE IF NOT EXISTS wrn_auth_passkeys ( id VARCHAR(191) PRIMARY KEY, user_id VARCHAR(191) NOT NULL, credential_id TEXT NOT NULL UNIQUE, public_key TEXT NOT NULL, counter BIGINT NOT NULL DEFAULT 0, transports_json TEXT NOT NULL, name VARCHAR(191) NOT NULL, created_at BIGINT NOT NULL, last_used_at BIGINT, backed_up INTEGER, device_type VARCHAR(64) ); CREATE INDEX IF NOT EXISTS wrn_auth_passkeys_user_idx ON wrn_auth_passkeys(user_id); CREATE TABLE IF NOT EXISTS wrn_auth_oauth_accounts ( id VARCHAR(191) PRIMARY KEY, user_id VARCHAR(191) NOT NULL, provider VARCHAR(64) NOT NULL, provider_account_id VARCHAR(191) NOT NULL, email VARCHAR(320), access_token TEXT, refresh_token TEXT, token_expires_at BIGINT, scope TEXT, created_at BIGINT NOT NULL, updated_at BIGINT NOT NULL, UNIQUE(provider, provider_account_id) ); CREATE TABLE IF NOT EXISTS wrn_auth_login_attempts ( id VARCHAR(191) PRIMARY KEY, identifier VARCHAR(320), user_id VARCHAR(191), success INTEGER NOT NULL, reason VARCHAR(191), ip VARCHAR(64), user_agent TEXT, created_at BIGINT NOT NULL, risk_score INTEGER NOT NULL, risk_level VARCHAR(32) NOT NULL ); CREATE INDEX IF NOT EXISTS wrn_auth_login_attempts_identifier_idx ON wrn_auth_login_attempts(identifier, created_at); CREATE TABLE IF NOT EXISTS wrn_auth_security_events ( id VARCHAR(191) PRIMARY KEY, user_id VARCHAR(191), type VARCHAR(191) NOT NULL, severity VARCHAR(32) NOT NULL, actor_user_id VARCHAR(191), session_id VARCHAR(191), ip VARCHAR(64), user_agent TEXT, created_at BIGINT NOT NULL, data_json TEXT ); CREATE INDEX IF NOT EXISTS wrn_auth_security_events_user_idx ON wrn_auth_security_events(user_id, created_at); -- +down DROP TABLE IF EXISTS wrn_auth_security_events; DROP TABLE IF EXISTS wrn_auth_login_attempts; DROP TABLE IF EXISTS wrn_auth_oauth_accounts; DROP TABLE IF EXISTS wrn_auth_passkeys; DROP TABLE IF EXISTS wrn_auth_recovery_codes; DROP TABLE IF EXISTS wrn_auth_totp_credentials; DROP TABLE IF EXISTS wrn_auth_otp_challenges; DROP TABLE IF EXISTS wrn_auth_tokens; DROP TABLE IF EXISTS wrn_auth_trusted_devices; DROP TABLE IF EXISTS wrn_auth_sessions; DROP TABLE IF EXISTS wrn_auth_password_credentials; DROP TABLE IF EXISTS wrn_auth_identities; DROP TABLE IF EXISTS wrn_auth_users;