Pre-requisitos
- Plano Enterprise (verificado em rota
/api/auth/sso/[tenantSlug]— outros planos retornam 403) - Dominio configurado (white-label opcional mas recomendado)
- Acesso admin ao IdP (Okta, Azure AD, Google Workspace, Ping, OneLogin, etc)
SAML 2.0
Configuracao
Settings > SSO > Add Provider > SAML
Campos:
- Display name — nome mostrado no botao "Sign in with..."
- Metadata XML URL OU Metadata XML colado — fornecido pelo IdP
- SP-initiated ou IdP-initiated — Studeia suporta ambos
- Allowed domains — restringe emails (anti-shadow accounts)
Endpoints expostos
- ACS (Assertion Consumer Service):
/api/auth/sso/[tenantSlug]/saml/callback - SP metadata:
/api/institution/sso/metadata
Validacao
SAML Response validado via node-saml:
- XML signature verification (anti-tampering)
- SAML XML namespace validation (anti-XXE)
- NotBefore/NotOnOrAfter checks
- Audience restriction
OIDC
Configuracao
Settings > SSO > Add Provider > OIDC
Campos:
- Discovery URL —
https://idp.com/.well-known/openid-configuration(auto-fetch) - Client ID + Client Secret (criptografado AES-256-GCM)
- Scopes — default:
openid profile email - PKCE — sempre on (S256 challenge method)
Fluxo
- Aluno clica "Sign in with [IdP]" →
/api/auth/sso/[tenantSlug]/oidc - Redirect para IdP com PKCE challenge + state HMAC-assinado + nonce em Redis (TTL 15min anti-replay)
- IdP autentica → callback
/api/auth/sso/[tenantSlug]/oidc/callback - Studeia valida state + nonce, troca code por tokens (PKCE verifier)
- Userinfo endpoint → JIT provisioning (cria User se nao existe) → Supabase session
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, com 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}
Configuracao no Azure AD (exemplo)
- Azure Portal > Enterprise Applications > Studeia > Provisioning
- Provisioning Mode: Automatic
- Tenant URL:
https://seuapp.studeia.com/api/scim/v2 - Secret Token: gerado no Studeia em Settings > SSO > SCIM Token (Bearer, criptografado no banco)
- Test Connection
- Mappings: atributos padrao (Microsoft Graph compativel)
- Settings > Scope: "Sync only assigned users and groups"
- Start provisioning
Group → Course mapping
Permite auto-matricula quando IdP adiciona usuario a grupo:
- Configure mapping em Settings > SSO > Groups
- Para cada SsoGroup, escolha Course alvo
- Quando SCIM adiciona membro ao grupo → Studeia cria Enrollment ativa no curso
- Quando remove → cancela matricula (status=cancelled)
- Grupos grandes (>50 membros): processamento via BullMQ (
scim-group-syncqueue), Response 202 + jobId
Seguranca
Tokens
- SCIM Bearer token: criptografado AES-256-GCM em
TenantSsoConfig.scimToken— NUNCA plain text - OAuth tokens (Google Workspace integration): mesmo padrao
- State OAuth: HMAC-SHA256 com
OAUTH_STATE_SECRET+ nonce Redis (TTL 15min)
JIT provisioning
allowedDomainsvalida email antes de criar usuario- Atributos do IdP mapeados via
SsoAttributeMapping - Role default configurable (
studentouteacher)
Auditoria
ScimAuditLog registra:
- Toda operacao SCIM (Create/Update/Delete) com timestamp + IP + actor (IdP) + user/group affected + payload (redacted)
- AdminAuditLog para mudancas em SsoConfig por admin institucional
Limitacoes
- LDAP direct (sem SAML/OIDC) nao suportado nativamente — roadmap
- Just-in-time deprovisioning via SAML SLO (Single Logout) parcial — recomenda-se usar SCIM para deprovisionamento confiavel
- Multi-factor authentication delegado ao IdP (Studeia nao gerencia MFA proprio quando SSO ativo)
- Group nesting (grupos dentro de grupos) suportado parcialmente — recomenda-se flatten no IdP