michangarrito/database/schemas/04-auth.sql
rckrdmrd 5a49ad0185 [INTEGRATION] feat: Integrate template-saas scopes and database objects
## Documentation
- Align MCH-029 to MCH-032 with template-saas modules (SAAS-008 to SAAS-015)
- Create MCH-034 (Analytics) and MCH-035 (Reports) from SAAS-016/017
- Update PLAN-DESARROLLO.md with Phase 7 and 8
- Update _MAP.md indexes (35 total epics)

## Database (5 new schemas, 14 tables)
- Add storage schema: buckets, files, signed_urls
- Add webhooks schema: endpoints, deliveries
- Add audit schema: logs, retention_policies
- Add features schema: flags, tenant_flags (14 seeds)
- Add analytics schema: metrics, events, reports, report_schedules
- Add auth.oauth_connections for MCH-030
- Add timestamptz_to_date() IMMUTABLE function
- Update EXPECTED_SCHEMAS in recreate-database.sh

## Analysis Reports
- ANALISIS-INTEGRACION-TEMPLATE-SAAS-2026-01-13.md
- VALIDACION-COHERENCIA-2026-01-13.md
- GAP-ANALYSIS-BD-2026-01-13.md
- REPORTE-EJECUCION-2026-01-13.md

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 07:10:55 -06:00

131 lines
3.9 KiB
SQL

-- =============================================================================
-- MICHANGARRITO - 04 AUTH
-- =============================================================================
-- Autenticación y usuarios
-- =============================================================================
-- Usuarios
CREATE TABLE IF NOT EXISTS auth.users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
-- Identificación
phone VARCHAR(20) NOT NULL,
email VARCHAR(100),
name VARCHAR(100) NOT NULL,
-- Autenticación
pin_hash VARCHAR(255),
biometric_enabled BOOLEAN DEFAULT false,
biometric_key TEXT,
-- Rol
role VARCHAR(20) NOT NULL DEFAULT 'owner',
permissions JSONB DEFAULT '{}',
-- Estado
status VARCHAR(20) DEFAULT 'active',
last_login_at TIMESTAMPTZ,
failed_attempts INTEGER DEFAULT 0,
locked_until TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(tenant_id, phone)
);
CREATE INDEX idx_users_tenant ON auth.users(tenant_id);
CREATE INDEX idx_users_phone ON auth.users(phone);
CREATE TRIGGER update_users_updated_at
BEFORE UPDATE ON auth.users
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
-- Sesiones
CREATE TABLE IF NOT EXISTS auth.sessions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
token_hash VARCHAR(255) NOT NULL,
refresh_token_hash VARCHAR(255),
device_type VARCHAR(20),
device_info JSONB,
ip_address VARCHAR(45),
expires_at TIMESTAMPTZ NOT NULL,
refresh_expires_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW(),
last_activity_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_sessions_user ON auth.sessions(user_id);
CREATE INDEX idx_sessions_token ON auth.sessions(token_hash);
-- Códigos OTP
CREATE TABLE IF NOT EXISTS auth.otp_codes (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
phone VARCHAR(20) NOT NULL,
code VARCHAR(6) NOT NULL,
purpose VARCHAR(20) NOT NULL,
attempts INTEGER DEFAULT 0,
max_attempts INTEGER DEFAULT 3,
expires_at TIMESTAMPTZ NOT NULL,
used_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_otp_phone ON auth.otp_codes(phone, purpose);
-- =============================================================================
-- OAUTH CONNECTIONS (MCH-030: Auth Social)
-- =============================================================================
-- Conexiones OAuth para login con Google/Apple
CREATE TABLE IF NOT EXISTS auth.oauth_connections (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
-- Proveedor OAuth
provider VARCHAR(20) NOT NULL, -- 'google', 'apple'
provider_user_id VARCHAR(255) NOT NULL,
-- Tokens
access_token TEXT,
refresh_token TEXT,
token_expires_at TIMESTAMPTZ,
-- Datos del perfil
email VARCHAR(255),
name VARCHAR(255),
avatar_url TEXT,
-- Datos crudos del proveedor
raw_data JSONB DEFAULT '{}',
-- Timestamps
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
-- Constraints
UNIQUE(provider, provider_user_id),
UNIQUE(user_id, provider)
);
CREATE INDEX idx_oauth_connections_user ON auth.oauth_connections(user_id);
CREATE INDEX idx_oauth_connections_provider ON auth.oauth_connections(provider, provider_user_id);
CREATE TRIGGER update_oauth_connections_updated_at
BEFORE UPDATE ON auth.oauth_connections
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
COMMENT ON TABLE auth.oauth_connections IS 'Conexiones OAuth para login social (Google, Apple)';
COMMENT ON COLUMN auth.oauth_connections.provider IS 'Proveedor OAuth: google, apple';
COMMENT ON COLUMN auth.oauth_connections.raw_data IS 'Datos JSON completos retornados por el proveedor';