Requisitos previos
- Plan Enterprise (verificado en la ruta
/api/auth/sso/[tenantSlug]— otros planes devuelven 403) - Dominio configurado (white-label opcional pero recomendado)
- Acceso de administrador al IdP (Okta, Azure AD, Google Workspace, Ping, OneLogin, etc.)
SAML 2.0
Configuración
Settings > SSO > Add Provider > SAML
Campos:
- Display name — nombre que aparece en el botón "Sign in with..."
- Metadata XML URL O Metadata XML pegado — proporcionado por el IdP
- SP-initiated o IdP-initiated — Studeia soporta ambos
- Allowed domains — restringe emails (anti-shadow accounts)
Endpoints expuestos
- ACS (Assertion Consumer Service):
/api/auth/sso/[tenantSlug]/saml/callback - SP metadata:
/api/institution/sso/metadata
Validación
La respuesta SAML se valida mediante node-saml:
- Verificación de XML signature (anti-tampering)
- Validación de namespace XML de SAML (anti-XXE)
- Comprobaciones de NotBefore/NotOnOrAfter
- Restricción de audiencia (Audience restriction)
OIDC
Configuración
Settings > SSO > Add Provider > OIDC
Campos:
- Discovery URL —
https://idp.com/.well-known/openid-configuration(auto-fetch) - Client ID + Client Secret (cifrado con AES-256-GCM)
- Scopes — por defecto:
openid profile email - PKCE — siempre activado (método de desafío S256)
Flujo
- El alumno hace clic en "Sign in with [IdP]" →
/api/auth/sso/[tenantSlug]/oidc - Redirección al IdP con PKCE challenge + state firmado con HMAC + nonce en Redis (TTL 15min anti-replay)
- El IdP autentica → callback
/api/auth/sso/[tenantSlug]/oidc/callback - Studeia valida state + nonce, intercambia el code por tokens (PKCE verifier)
- Endpoint Userinfo → JIT provisioning (crea User si no existe) → sesión Supabase
SCIM 2.0
14 endpoints RFC 7644
GET /api/scim/v2/ServiceProviderConfig
GET /api/scim/v2/ResourceTypes
GET /api/scim/v2/Schemas
GET /api/scim/v2/Users (list, con filter)
POST /api/scim/v2/Users (create)
GET /api/scim/v2/Users/{id}
PUT /api/scim/v2/Users/{id} (replace full)
PATCH /api/scim/v2/Users/{id} (partial — Microsoft Graph compat)
DELETE /api/scim/v2/Users/{id} (soft-delete: User.status=suspended)
GET /api/scim/v2/Groups (list)
POST /api/scim/v2/Groups
GET /api/scim/v2/Groups/{id}
PUT /api/scim/v2/Groups/{id}
PATCH /api/scim/v2/Groups/{id}
DELETE /api/scim/v2/Groups/{id}
Configuración en Azure AD (ejemplo)
- Azure Portal > Enterprise Applications > Studeia > Provisioning
- Provisioning Mode: Automatic
- Tenant URL:
https://tuapp.studeia.com/api/scim/v2 - Secret Token: generado en Studeia en Settings > SSO > SCIM Token (Bearer, cifrado en la base de datos)
- Test Connection
- Mappings: atributos estándar (compatible con Microsoft Graph)
- Settings > Scope: "Sync only assigned users and groups"
- Start provisioning
Group → Course mapping
Permite la matrícula automática cuando el IdP agrega un usuario a un grupo:
- Configura el mapping en Settings > SSO > Groups
- Para cada SsoGroup, elige el Course destino
- Cuando SCIM agrega un miembro al grupo → Studeia crea un Enrollment activo en el curso
- Cuando lo elimina → cancela la matrícula (status=cancelled)
- Grupos grandes (>50 miembros): procesamiento mediante BullMQ (cola
scim-group-sync), Response 202 + jobId
Seguridad
Tokens
- SCIM Bearer token: cifrado con AES-256-GCM en
TenantSsoConfig.scimToken— NUNCA en texto plano - Tokens OAuth (integración con Google Workspace): mismo estándar
- State OAuth: HMAC-SHA256 con
OAUTH_STATE_SECRET+ nonce en Redis (TTL 15min)
JIT provisioning
allowedDomainsvalida el email antes de crear el usuario- Atributos del IdP mapeados mediante
SsoAttributeMapping - Role por defecto configurable (
studentoteacher)
Auditoría
ScimAuditLog registra:
- Toda operación SCIM (Create/Update/Delete) con timestamp + IP + actor (IdP) + usuario/grupo afectado + payload (redactado)
- AdminAuditLog para cambios en SsoConfig realizados por un administrador institucional
Limitaciones
- LDAP directo (sin SAML/OIDC) no soportado de forma nativa — en el roadmap
- Just-in-time deprovisioning mediante SAML SLO (Single Logout) parcial — se recomienda usar SCIM para un desaprovisionamiento confiable
- Autenticación multifactor delegada al IdP (Studeia no gestiona MFA propio cuando SSO está activo)
- Group nesting (grupos dentro de grupos) soportado parcialmente — se recomienda hacer flatten en el IdP