-- ============================================================================ -- OrbiQuant IA - OAuth Providers -- ============================================================================ -- Archivo: 01b_oauth_providers.sql -- Descripción: Proveedores OAuth y vinculación de cuentas sociales -- Fecha: 2025-12-05 -- ============================================================================ SET search_path TO public; -- ============================================================================ -- ENUM: auth_provider_enum -- ============================================================================ CREATE TYPE auth_provider_enum AS ENUM ( 'email', -- Email/password tradicional 'phone', -- Teléfono (SMS/WhatsApp) 'google', -- Google OAuth 'facebook', -- Facebook OAuth 'twitter', -- X (Twitter) OAuth 'apple', -- Apple Sign In 'github' -- GitHub OAuth ); -- ============================================================================ -- TABLA: oauth_accounts -- Descripción: Cuentas OAuth vinculadas a usuarios -- ============================================================================ CREATE TABLE IF NOT EXISTS oauth_accounts ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, -- Proveedor provider auth_provider_enum NOT NULL, provider_account_id VARCHAR(255) NOT NULL, -- Tokens OAuth access_token TEXT, refresh_token TEXT, token_expires_at TIMESTAMPTZ, token_type VARCHAR(50), scope TEXT, -- Datos del perfil del proveedor provider_email VARCHAR(255), provider_name VARCHAR(255), provider_avatar_url TEXT, provider_profile JSONB, -- Timestamps created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, -- Constraints UNIQUE(provider, provider_account_id), UNIQUE(user_id, provider) ); CREATE INDEX idx_oauth_user_id ON oauth_accounts(user_id); CREATE INDEX idx_oauth_provider ON oauth_accounts(provider); CREATE INDEX idx_oauth_provider_account ON oauth_accounts(provider, provider_account_id); -- ============================================================================ -- TABLA: phone_verifications -- Descripción: Verificaciones de teléfono (OTP via SMS/WhatsApp) -- ============================================================================ CREATE TYPE phone_channel_enum AS ENUM ('sms', 'whatsapp', 'call'); CREATE TABLE IF NOT EXISTS phone_verifications ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), -- Teléfono phone_number VARCHAR(20) NOT NULL, country_code VARCHAR(5) NOT NULL, -- OTP otp_code VARCHAR(6) NOT NULL, otp_hash VARCHAR(255) NOT NULL, channel phone_channel_enum DEFAULT 'whatsapp', -- Estado verified BOOLEAN DEFAULT FALSE, attempts INT DEFAULT 0, max_attempts INT DEFAULT 3, -- Vinculación user_id UUID REFERENCES users(id) ON DELETE SET NULL, purpose VARCHAR(50) NOT NULL DEFAULT 'login', -- login, register, verify, 2fa -- Validez expires_at TIMESTAMPTZ NOT NULL, verified_at TIMESTAMPTZ, -- Timestamps created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX idx_phone_verif_phone ON phone_verifications(phone_number); CREATE INDEX idx_phone_verif_expires ON phone_verifications(expires_at); CREATE INDEX idx_phone_verif_user ON phone_verifications(user_id); -- ============================================================================ -- TABLA: email_verifications -- Descripción: Tokens de verificación de email -- ============================================================================ CREATE TABLE IF NOT EXISTS email_verifications ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), -- Email email VARCHAR(255) NOT NULL, -- Token token VARCHAR(255) NOT NULL UNIQUE, token_hash VARCHAR(255) NOT NULL, -- Vinculación user_id UUID REFERENCES users(id) ON DELETE CASCADE, purpose VARCHAR(50) NOT NULL DEFAULT 'verify', -- verify, reset_password, change_email -- Estado used BOOLEAN DEFAULT FALSE, -- Validez expires_at TIMESTAMPTZ NOT NULL, used_at TIMESTAMPTZ, -- Timestamps created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX idx_email_verif_email ON email_verifications(email); CREATE INDEX idx_email_verif_token ON email_verifications(token_hash); CREATE INDEX idx_email_verif_user ON email_verifications(user_id); -- ============================================================================ -- TABLA: auth_logs -- Descripción: Logs de eventos de autenticación -- ============================================================================ CREATE TYPE auth_event_enum AS ENUM ( 'login_success', 'login_failed', 'logout', 'register', 'password_reset_request', 'password_reset_complete', 'email_verified', 'phone_verified', 'oauth_linked', 'oauth_unlinked', '2fa_enabled', '2fa_disabled', '2fa_verified', 'session_revoked', 'account_locked', 'account_unlocked' ); CREATE TABLE IF NOT EXISTS auth_logs ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), user_id UUID REFERENCES users(id) ON DELETE SET NULL, -- Evento event auth_event_enum NOT NULL, provider auth_provider_enum, -- Contexto ip_address INET, user_agent TEXT, device_fingerprint VARCHAR(255), location JSONB, -- {country, city, region} -- Metadata metadata JSONB, -- Resultado success BOOLEAN DEFAULT TRUE, error_message TEXT, -- Timestamps created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX idx_auth_logs_user ON auth_logs(user_id); CREATE INDEX idx_auth_logs_event ON auth_logs(event); CREATE INDEX idx_auth_logs_created ON auth_logs(created_at DESC); CREATE INDEX idx_auth_logs_ip ON auth_logs(ip_address); -- ============================================================================ -- MODIFICAR TABLA users: agregar campo auth_provider -- ============================================================================ ALTER TABLE users ADD COLUMN IF NOT EXISTS primary_auth_provider auth_provider_enum DEFAULT 'email'; -- ============================================================================ -- TRIGGERS -- ============================================================================ CREATE TRIGGER update_oauth_accounts_updated_at BEFORE UPDATE ON oauth_accounts FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); -- ============================================================================ -- FUNCIONES DE UTILIDAD -- ============================================================================ -- Función para generar OTP de 6 dígitos CREATE OR REPLACE FUNCTION generate_otp() RETURNS VARCHAR(6) AS $$ BEGIN RETURN LPAD(FLOOR(RANDOM() * 1000000)::TEXT, 6, '0'); END; $$ LANGUAGE plpgsql; -- Función para limpiar verificaciones expiradas CREATE OR REPLACE FUNCTION cleanup_expired_verifications() RETURNS void AS $$ BEGIN DELETE FROM phone_verifications WHERE expires_at < NOW() AND verified = FALSE; DELETE FROM email_verifications WHERE expires_at < NOW() AND used = FALSE; END; $$ LANGUAGE plpgsql;