MFA enrollment and enforcement in server (deferred from auth harmonization) #154
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Deferred from the 2026-04-26 auth harmonization spec. Captured here so it isn't lost.
Background
The cameleer-saas team shipped per-tenant MFA in 2026-04-26 (
cameleer-saas/docs/superpowers/specs/2026-04-26-password-reset-mfa-design.md) and produced a handoff doc for the server (2026-04-26-server-mfa-handoff.md). The handoff specified:mfa_enrolledclaim from JWT, queryGET /platform/api/tenant/{slug}/mfa-policy(5 min cache), return403withX-Cameleer-Error: APP_MFA_REQUIREDon enforce-but-not-enrolled.This was deferred during the harmonization spec because it would:
The access model means tenant
owners can use the existing SaaS portal MFA UI, butoperators andviewers — who skip the portal and land directly on the server dashboard — have no enrollment surface at all. So enforcement was deferred until enrollment for these users is solved.Scope when this is picked up
Either of:
(A) IdP-agnostic redirect model — server detects
APP_MFA_REQUIRED, redirects to a configurable enrollment URL. SaaS adds an/me/securityself-service page accessible to all org members (operator/viewer included), keeping/tenantportal-admin-only. Server stays Logto-agnostic.(B) Server-hosted enrollment per the handoff doc — server gets a "Profile / Security" page calling Logto Management API directly. Requires SaaS provisioner to inject
CAMELEER_SERVER_LOGTO_M2M_CLIENTID/_CLIENTSECRET. Couples the server to Logto.Recommend (A) — keeps the server's identity story clean ("we consume tokens, we don't manage credentials") and matches how standalone-with-Keycloak / Auth0 already works (MFA is the IdP's job).
Server work when picked up (assuming A)
CAMELEER_SERVER_SAAS_PLATFORMURL— base for enrollment redirect URL (and the mfa-policy callback).mfaEnrollmentUrltemplate (per-deployment override).MfaEnforcementFilter— Spring filter, readsmfa_enrolledfrom JWT, caches tenant policy fromGET /platform/api/tenant/{slug}/mfa-policy(5 min TTL), returns theAPP_MFA_REQUIRED403 envelope.ui/src/api/client.tsthat recognises theX-Cameleer-Error: APP_MFA_REQUIREDheader and redirects to the configured enrollment URL./api/v1/health,/api/v1/auth/**, public assets, the enrollment-redirect handler itself.Out of scope (separate issues)
References
cameleer-saas/docs/superpowers/specs/2026-04-26-password-reset-mfa-design.mdcameleer-saas/docs/superpowers/specs/2026-04-26-server-mfa-handoff.mdcameleer-server/docs/superpowers/specs/2026-04-26-auth-harmonization-design.md(the harmonization spec that deferred this)