# MFA Setup Reference Step-by-step setup for enabling MFA on Experience Sites. Read this when executing Steps 3-5 of the SKILL.md workflow. ## Deploy Permission Sets Deploy from the skill's `assets/` directory or from your project's `force-app/main/default/permissionsets/`: ```bash sf project deploy start --source-dir force-app/main/default/permissionsets \ --target-org --test-level NoTestRun ``` This deploys: - **MFA_Required_For_Community** — enforces MFA via `ForceTwoFactor` - **API_Enabled_For_Community** — grants API access (always deployed for React sites) ## Grant Guest Profile Apex Class Access The site login page runs as the **guest user**. If the guest profile lacks access to the UIBundle login Apex classes, users hit `FORBIDDEN: You do not have access to the Apex class named: UIBundleLogin` and never reach the MFA challenge (SKILL.md Step 3c). Resolve `` from the guest-user query in Step 3c, then run this anonymous Apex. It diffs the guest permission set's existing class access against the six login classes and inserts only the missing `SetupEntityAccess` entries (safe to re-run): ```apex // Run via: sf apex run --target-org Profile guestProfile = [SELECT Id FROM Profile WHERE Name = '']; PermissionSet guestPs = [SELECT Id FROM PermissionSet WHERE ProfileId = :guestProfile.Id AND IsOwnedByProfile = true]; List loginClasses = [SELECT Id, Name FROM ApexClass WHERE Name IN ('UIBundleLogin', 'UIBundleAuthUtils', 'UIBundleForgotPassword', 'UIBundleRegistration', 'UIBundleSocialLoginConfig', 'UIBundleChangePassword')]; Set existingAccess = new Set(); for (SetupEntityAccess sea : [SELECT SetupEntityId FROM SetupEntityAccess WHERE ParentId = :guestPs.Id AND SetupEntityType = 'ApexClass']) { existingAccess.add(sea.SetupEntityId); } List toInsert = new List(); for (ApexClass c : loginClasses) { if (!existingAccess.contains(c.Id)) { System.debug('Missing: ' + c.Name); toInsert.add(new SetupEntityAccess(ParentId = guestPs.Id, SetupEntityId = c.Id)); } } if (!toInsert.isEmpty()) { insert toInsert; System.debug('Granted access to ' + toInsert.size() + ' classes'); } ``` Alternatively, deploy `` entries (`enabled=true`) for the same six classes to the guest profile metadata XML. ## Assign Permission Sets ### Option A: Individual users (testing) ```bash # Find community users sf data query --target-org \ --query "SELECT Id, Username, Name, Profile.Name FROM User WHERE UserType IN ('CspLitePortal', 'PowerCustomerSuccess', 'CustomerSuccess') AND IsActive = true" --json # Get permission set IDs sf data query --target-org \ --query "SELECT Id, Name FROM PermissionSet WHERE Name IN ('MFA_Required_For_Community', 'API_Enabled_For_Community')" --json # Assign MFA permission set sf data create record --target-org --sobject PermissionSetAssignment \ --values "AssigneeId='' PermissionSetId=''" --json # Assign API Enabled sf data create record --target-org --sobject PermissionSetAssignment \ --values "AssigneeId='' PermissionSetId=''" --json ``` ### Option B: Permission Set Group (production) 1. Setup > Permission Set Groups > New 2. Add `MFA_Required_For_Community` 3. Add `API_Enabled_For_Community` 4. Assign the group to the community profile ### Option C: CLI bulk assign ```bash sf org assign permset --name MFA_Required_For_Community --target-org --json sf org assign permset --name API_Enabled_For_Community --target-org --json ``` ## Publish and Verify ```bash sf community publish --name "" --target-org ``` Verification steps: 1. Open an incognito/private browser window 2. Navigate to the site login page 3. Enter credentials and submit 4. MFA challenge page appears (on the `vforcesite` domain) 5. Complete verification (register authenticator app if first time) 6. Land on the site, fully authenticated ## Customize Branding (Optional) The MFA challenge page uses `NetworkBranding` metadata. For React Experience Sites (Site Containers), the branding admin UI is available in Setup → Digital Experiences → Login Settings (since 264). The Metadata API path below is the option for scripted/repeatable setup. ```bash # Retrieve existing branding sf project retrieve start --target-org \ --metadata NetworkBranding --target-metadata-dir ./branding --unzip # Edit networkBranding-meta.xml: loginLogo, loginFooterText, loginPrimaryColor # Deploy updated branding sf project deploy start --target-org \ --source-dir force-app/main/default/networkBranding --test-level NoTestRun ``` ## Troubleshooting | Symptom | Cause | Fix | |---------|-------|-----| | No MFA challenge on login | `ForceTwoFactor` not assigned | Verify `MFA_Required_For_Community` assignment | | `API_DISABLED_FOR_ORG` after login | Missing `ApiEnabled` | Assign `API_Enabled_For_Community` | | Default Salesforce branding | No `NetworkBranding` deployed | Deploy custom branding via Metadata API | | `vforcesite` in MFA URL | Expected behavior | Platform serves login/MFA from Force.com Site domain | | MFA works but site errors | Profile missing page/object access | Check community profile permissions | ## Verification Commands Confirm permission set exists and has the right permissions: ```bash sf data query --target-org \ --query "SELECT Name, PermissionsForceTwoFactor FROM PermissionSet WHERE Name = 'MFA_Required_For_Community'" --json ``` Confirm assignment: ```bash sf data query --target-org \ --query "SELECT Assignee.Username, PermissionSet.Name FROM PermissionSetAssignment WHERE PermissionSet.Name = 'MFA_Required_For_Community'" --json ```