# Cameleer-Server MFA Handoff Document **Date:** 2026-04-26 **For:** cameleer-server team **Context:** The SaaS platform now supports TOTP MFA with backup codes. This document specifies what the server team needs to implement for MFA enrollment in the server UI. ## 1. JWT Claim: `mfa_enrolled` Every access token now includes an `mfa_enrolled: boolean` claim, set by the Logto Custom JWT script. The server already parses JWT claims for the `roles` field — `mfa_enrolled` works identically. **Example decoded JWT payload:** ```json { "sub": "user-id-123", "roles": ["server:admin"], "mfa_enrolled": true, "aud": "https://api.cameleer.local", "scope": "tenant:manage tenant:view" } ``` ## 2. Enforcement ### When to enforce Check whether the tenant requires MFA: - **Endpoint:** `GET /platform/api/tenant/{slug}/mfa-policy` - **Auth:** M2M token (same as existing server -> SaaS API calls) - **Response:** `{ "mfaRequired": true/false }` - **Cache:** 5-minute TTL recommended ### How to enforce On authenticated requests, if `mfaRequired` is `true` and the JWT `mfa_enrolled` claim is `false`: **Response:** ``` HTTP 403 X-Cameleer-Error: APP_MFA_REQUIRED Content-Type: application/json { "error": "APP_MFA_REQUIRED", "code": "mfa_enrollment_required", "message": "Your organization requires multi-factor authentication" } ``` **Exempt paths:** MFA enrollment endpoints (below), health checks, public assets. The server UI should intercept 403 responses with `X-Cameleer-Error: APP_MFA_REQUIRED` and redirect to the MFA enrollment page. ## 3. MFA Enrollment API The server needs to call Logto's Management API to manage MFA for users. Use the existing M2M token for authentication. ### Get MFA status ``` GET https://{logto-endpoint}/api/users/{userId}/mfa-verifications Authorization: Bearer {m2m_token} Response: [ { "id": "ver-123", "type": "Totp", "createdAt": "..." }, { "id": "ver-456", "type": "BackupCode", "createdAt": "..." } ] ``` ### Generate TOTP secret Generate a 20-byte random secret, Base32-encode it, and create a QR code URI: ``` otpauth://totp/Cameleer:{userEmail}?secret={base32Secret}&issuer=Cameleer ``` Show the QR code to the user. After they scan and provide a 6-digit code, verify it server-side using the TOTP algorithm (RFC 6238, HMAC-SHA1, 30-second window, +/-1 step drift). ### Bind TOTP to user After successful verification: ``` POST https://{logto-endpoint}/api/users/{userId}/mfa-verifications Authorization: Bearer {m2m_token} Content-Type: application/json { "type": "Totp", "secret": "{base32Secret}" } Response: { "type": "Totp", "secret": "...", "secretQrCode": "..." } ``` ### Generate backup codes After TOTP is bound: ``` POST https://{logto-endpoint}/api/users/{userId}/mfa-verifications Authorization: Bearer {m2m_token} Content-Type: application/json { "type": "BackupCode" } Response: { "type": "BackupCode", "codes": ["abc123", "def456", ...] } ``` Display the 10 codes once. User must acknowledge saving them before dismissing. ### Remove MFA (admin action) ``` DELETE https://{logto-endpoint}/api/users/{userId}/mfa-verifications/{verificationId} Authorization: Bearer {m2m_token} ``` Remove all verifications (TOTP + BackupCode) to fully reset MFA for a user. ## 4. UX Requirements ### Enrollment flow 1. User clicks "Set up MFA" in settings 2. Show QR code (200x200px) with the TOTP secret URI 3. User scans with authenticator app 4. User enters 6-digit verification code 5. On success -> show 10 backup codes in a 2-column monospace grid 6. "Copy all" and "Download .txt" buttons 7. Checkbox: "I've saved my backup codes" — must be checked before dismissing 8. After dismissal, force token refresh to get `mfa_enrolled: true` in JWT ### Enrolled state - Show "Authenticator app configured" with green status badge - "Regenerate backup codes" button - "Remove MFA" button with confirmation dialog ### Backup code fallback (sign-in) This is handled by the SaaS custom sign-in UI, not the server. No server changes needed for the sign-in flow. ## 5. Error States | Scenario | Response | |----------|----------| | User already has TOTP enrolled | 422 — "TOTP already configured" | | Invalid TOTP code during setup | Show error, let user retry | | Backup code already used (sign-in) | Handled by SaaS sign-in UI | | All backup codes exhausted | Admin removes MFA via team page | | Remove MFA while enforcement active | User will be prompted to re-enroll on next request |