关于 experience-ui-bundle-mfa-configure
此技能指导为 Salesforce Experience Site 社区用户启用 MFA,涵盖权限集部署、分配及登录流程验证,并检查常见站点访问配置问题。
主要功能
- 01部署 MFA 与 API 权限集
- 02分配权限集给社区用户
- 03检查网络成员和访客 Apex 权限
- 04排查 MFA 登录挑战
- 05自定义登录页品牌设置
适用场景
- 01为 Experience Site 用户启用 MFA
- 02验证登录时是否显示 MFA 挑战
- 03排查站点登录或 MFA 未触发问题
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ forcedotcom/ sf-skills --skill experience-ui-bundle-mfa-configure安装后在 Claude Code 中输入/experience-ui-bundle-mfa-configure确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
assets/API_Enabled_For_Community.permissionset-meta.xmlMFA_Required_For_Community.permissionset-meta.xmlreferences/branding.mdsetup.mdsocial-login.md详细介绍
在 Experience Sites 上启用 MFA
为 Experience Site(社区)用户启用多因素身份验证:部署正确的权限集,并验证由平台处理的 MFA 验证流程。
范围
范围内:
- 为社区用户部署
ForceTwoFactor权限集 - 部署
ApiEnabled权限集(进行登录后 API 调用所必需) - 为社区用户分配权限集
- 排查登录时未显示 MFA 的问题
- 通过 NetworkBranding 元数据自定义 MFA/登录页面品牌
范围外 — 请交由其他工具处理:
- 构建自定义登录界面 →
experience-ui-bundle-frontend-generate - 创建通用权限集 →
platform-permission-set-generate - 分配权限集(如果已部署)→
dx-org-permission-set-assign - 将元数据部署到 org →
platform-metadata-deploy - 为组织范围内的 Salesforce 内部用户启用 MFA → Setup > Identity Verification(非技能范围)
前提条件
使用此技能前,请确保以下条件已满足:
| 前提条件 | 原因 |
|---|---|
| Experience Cloud 站点已部署并激活 | MFA 适用于社区登录;没有站点,就没有可保护的登录流程 |
| 社区用户已存在(或将自行注册) | 权限集会分配给社区用户;站点必须具有启用了社区功能的配置文件 |
| 已启用 Customer Community 或 Customer Community Login 许可证 | 社区用户配置文件需要此许可证;没有许可证,用户创建和配置文件部署都会失败 |
| Network/站点至少已发布过一次 | 站点必须能通过其 URL 访问,登录时才会显示 MFA 验证 |
注意: 此技能不处理组织设置、许可证配置或 Experience Cloud 站点创建。如果缺少这些前提条件,请先通过 Setup > Digital Experiences > All Sites > New 设置,或部署站点的基础应用包。
必需输入
执行前请收集以下信息:
| 输入 | 如何确定 |
|---|---|
| 目标 org | sf CLI 命令使用的 org 别名 |
| 站点名称 | Experience Site(Network)名称 — 通过 SELECT Id, Name FROM Network 查询确定(参见步骤 1);这是站点/Network 名称,而不是 uiBundles/ 应用名称 |
| 社区用户 | 要为哪些用户或配置文件分配 MFA |
关键领域知识
以下事实不易察觉,且经常引起混淆:
| 事实 | 详情 |
|---|---|
| 无需自定义界面 | 平台会呈现 MFA 验证页面 — 无需 React/LWC 组件 |
| ForceTwoFactor 权限 | 在登录时为社区用户强制启用 MFA 的唯一方式 |
| 组织身份验证复选框 | 不会为社区/门户用户强制启用 MFA — 仅适用于内部用户 |
| vforcesite 域名 | MFA 验证页面始终由底层 Force.com Site 域名提供 — 这是预期行为 |
| 始终部署 ApiEnabled | React Experience Sites 会进行登录后 REST/Connect API 调用(sdk.graphql、sdk.fetch);没有 ApiEnabled 时,这些调用会因 API_DISABLED_FOR_ORG 而失败 |
| 社交登录/SSO 与 MFA 相互独立 | React 站点通过内置的 Social Login 组件呈现已配置的 Auth Providers(随 264 发布);这由 Auth Provider 设置驱动,而非由 MFA 权限集驱动。请参阅 references/social-login.md。 |
| React 站点支持登录页面品牌定制 | 自 264 起,Setup 中会为 Site Containers 显示 NetworkBranding 的“Login & Registration”部分,因此可在界面中自定义徽标/颜色/页脚 — 元数据 API 仍然可用。 |
工作流
步骤 1:解析目标站点(Network)
这些是 React Experience Site,因此始终会部署两组权限集 —
ForceTwoFactor(强制执行 MFA)和 ApiEnabled(React 站点会在登录后调用 API)。
从组织中解析 Experience Site 的真实名称和 ID — 不要假设 uiBundles/ 应用文件夹名称就是站点名称。两者经常不同,而且站点名称必须来自组织(部署目标),而不是本地项目。下面的 <site-name> 和 <NETWORK_ID> 由此得出:
sf data query --target-org <org-alias> \
--query "SELECT Id, Name FROM Network" --json
- 只有一个站点 → 使用其
Name作为<site-name>,使用其Id作为<NETWORK_ID>。 - 有多个站点 → 询问用户要使用哪一个(显示名称)。
- 没有站点 → 站点尚未部署;停止并告知用户(参见先决条件)。
步骤 2:生成权限集文件
首先,检测项目的源目录:
jq -r '.packageDirectories[0].path + "/main/default"' sfdx-project.json
将结果用作 <source-dir>(例如 force-app/main/default),用于下面的所有命令。
编写两组权限集(React Experience Site 始终需要两组):
- 读取
assets/MFA_Required_For_Community.permissionset-meta.xml - 将其写入用户项目中的
<source-dir>/permissionsets/MFA_Required_For_Community.permissionset-meta.xml - 读取
assets/API_Enabled_For_Community.permissionset-meta.xml - 将其写入
<source-dir>/permissionsets/API_Enabled_For_Community.permissionset-meta.xml
步骤 3:部署到组织
sf project deploy start \
--source-dir <source-dir>/permissionsets \
--target-org <org-alias> --test-level NoTestRun
步骤 3b:验证社区配置文件是否为网络成员
向用户分配权限集之前,请确认社区配置文件已注册为站点成员。否则,社区用户将完全无法登录(并且不会触发 MFA)。
- 查询当前网络成员:
sf data query --target-org <org-alias> \
--query "SELECT Id, ParentId FROM NetworkMemberGroup WHERE NetworkId = '<NETWORK_ID>'" --json
- 检查社区配置文件是否在列表中:
sf data query --target-org <org-alias> \
--query "SELECT Id, Name FROM Profile WHERE UserType IN ('CspLitePortal', 'PowerCustomerSuccess') AND Name LIKE '%Community%'" --json
- 如果该配置文件不是成员,将其添加到
.network-meta.xml:
<networkMemberGroups>
<profile>YOUR_COMMUNITY_PROFILE_NAME</profile>
</networkMemberGroups>
- 部署更新后的网络元数据:
sf project deploy start \
--source-dir <source-dir>/networks \
--target-org <org-alias> --test-level NoTestRun
IMPORTANT: 如果社区配置文件不是网络成员,使用该配置文件的用户将无法登录 — 这意味着,即使权限集分配正确,也不会触发 MFA。这是新部署的组织中常见的配置错误。
步骤 3c:验证访客配置文件是否具有登录所需的 Apex 类访问权限
站点登录页面以访客用户(未通过身份验证)的身份运行。如果访客配置文件无权访问登录所需的 Apex 类,用户将收到 FORBIDDEN: You do not have access to the Apex class named: UIBundleLogin,并且永远无法进入 MFA 验证环节。
- 查找站点访客用户配置文件:
sf data query --target-org <org-alias> \
--query "SELECT Id, Username, Profile.Name, Profile.Id FROM User WHERE UserType = 'Guest' AND IsActive = true" --json
- 授予对缺失的 UIBundle 登录类的访问权限。 六个类分别是
UIBundleLogin、UIBundleAuthUtils、UIBundleForgotPassword、UIBundleChangePassword、UIBundleRegistration和UIBundleSocialLoginConfig。运行references/setup.md中的匿名 Apex(“Grant Guest Profile Apex Class Access”)——该脚本会比对现有访问权限,并且只插入缺少的权限——或者将相同类的<classAccess>条目部署到访客配置文件元数据 XML 中。
IMPORTANT: 这与 MFA 无关,但如果缺少此权限,登录页面本身就无法正常运行。技能必须验证这一点,确保 MFA 能够触发。在新部署的组织中,访客配置文件未获得完整的类访问权限是常见问题。
步骤 4:分配权限集
查找社区用户:
sf data query --target-org <org-alias> \
--query "SELECT Id, Username, Name, Profile.Name FROM User WHERE UserType IN ('CspLitePortal', 'PowerCustomerSuccess', 'CustomerSuccess') AND IsActive = true" --json
如果存在社区用户:
查找权限集 ID:
sf data query --target-org <org-alias> \
--query "SELECT Id, Name FROM PermissionSet WHERE Name IN ('MFA_Required_For_Community', 'API_Enabled_For_Community')" --json
分配给每位用户:
sf data create record --target-org <org-alias> --sobject PermissionSetAssignment \
--values "AssigneeId='<USER_ID>' PermissionSetId='<PERM_SET_ID>'" --json
或者,委托给 dx-org-permission-set-assign 技能:
sf org assign permset --name MFA_Required_For_Community --target-org <org-alias> --json
sf org assign permset --name API_Enabled_For_Community --target-org <org-alias> --json
如果未找到社区用户:
向用户询问:"此组织中未找到活跃的社区用户。是否要我创建一个测试社区用户,以便你验证 MFA 是否正常工作?"
如果用户同意,请创建一个测试社区用户:
- 从站点的网络配置中查找社区配置文件:
sf data query --target-org <org-alias> \
--query "SELECT Id, Name FROM Profile WHERE UserType IN ('CspLitePortal', 'PowerCustomerSuccess') AND Name LIKE '%Customer Community%'" --json
- 创建一个帐户(社区用户的必需父级):
sf data create record --target-org <org-alias> --sobject Account \
--values "Name='MFA Test Account'" --json
- 创建一个联系人(关联到该帐户):
sf data create record --target-org <org-alias> --sobject Contact \
--values "FirstName='MFA' LastName='Test User' Email='mfa.testuser@<site-name>.test' AccountId='<ACCOUNT_ID>'" --json
- 使用社区配置文件创建用户:
sf data create record --target-org <org-alias> --sobject User \
--values "FirstName='MFA' LastName='Test User' Email='mfa.testuser@<site-name>.test' Username='mfa.testuser@<site-name>.test' Alias='mfatest' ProfileId='<PROFILE_ID>' ContactId='<CONTACT_ID>' EmailEncodingKey='UTF-8' LanguageLocaleKey='en_US' LocaleSidKey='en_US' TimeZoneSidKey='America/Los_Angeles'" --json
- 为测试用户设置密码:
sf data update record --target-org <org-alias> --sobject User \
--where "Username='mfa.testuser@<site-name>.test'" \
--values "IsActive=true" --json
sf org generate password --target-org <org-alias> --on-behalf-of mfa.testuser@<site-name>.test --json
- 为新用户分配两个权限集:
sf org assign permset --name MFA_Required_For_Community --target-org <org-alias> --on-behalf-of mfa.testuser@<site-name>.test --json
sf org assign permset --name API_Enabled_For_Community --target-org <org-alias> --on-behalf-of mfa.testuser@<site-name>.test --json
向用户报告凭据,以便其进行测试:
"已创建测试用户:
mfa.testuser@<site-name>.test,密码为:<generated-password>。你可以使用这些凭据验证站点上的 MFA。"
**IMPORTANT:**社区用户必须遵循帐户 → 联系人 → 用户层级。在社区配置文件下创建未关联联系人的用户将会失败。
步骤 5:将权限集注册为站点成员(networkMemberGroups)
networkMemberGroups是一个成员资格/访问门槛——它列出持有者会被视为站点成员的配置文件和 权限集。它不会为用户分配 MFA。分配操作在步骤 4 中完成(针对每个用户);请在此处注册权限集, 以便已分配权限集的用户仍被视为站点成员。
- 在项目中查找现有的
.network-meta.xml:
find . -name "*.network-meta.xml" -not -path "*/node_modules/*"
-
读取文件并找到
<networkMemberGroups>部分。 -
添加权限集条目(如果尚未存在):
<networkMemberGroups>
<profile>YOUR_COMMUNITY_PROFILE_NAME</profile>
<permissionSet>MFA_Required_For_Community</permissionSet>
<permissionSet>API_Enabled_For_Community</permissionSet>
</networkMemberGroups>
**IMPORTANT:**网络元数据部署是声明式的——部署的内容会成为完整状态。请勿从头创建新的
.network-meta.xml。务必读取现有文件并向其中添加条目。
- 部署更新后的网络元数据:
sf project deploy start \
--source-dir <source-dir>/networks \
--target-org <org-alias> --test-level NoTestRun
步骤 6:发布并验证
sf community publish --name "<site-name>" --target-org <org-alias>
验证步骤:
- 打开无痕浏览器
- 前往站点登录页面
- 输入凭据 → 应显示 MFA 验证页面(位于 vforcesite 域名)
- 完成 MFA → 应进入站点并处于登录状态
规则
| 规则 | 原因 |
|---|---|
| 切勿使用组织范围的 Identity Verification 复选框来启用社区 MFA | 它只影响内部用户——对社区登录没有影响 |
React 站点始终部署 ApiEnabled | 没有它,登录后的 API 调用(sdk.graphql、sdk.fetch)会失败 |
| 权限集名称必须完全一致——不要重命名 | MFA_Required_For_Community 和 API_Enabled_For_Community 是规范名称 |
| 不要构建自定义 MFA UI 组件 | 平台会处理整个 MFA 验证流程——自定义 UI 会造成重复和冲突 |
| 测试前务必先分配 | 仅部署不会激活 MFA——必须将其分配给特定用户 |
注意事项
| 症状 | 原因 | 解决方法 |
|---|---|---|
| 登录时没有 MFA 验证挑战 | 未向用户分配 ForceTwoFactor 权限 | 确认该用户存在 PermissionSetAssignment |
登录后出现 API_DISABLED_FOR_ORG | 缺少 ApiEnabled 权限 | 分配 API_Enabled_For_Community 权限集 |
| MFA 页面显示默认的 Salesforce 品牌样式 | 未部署 NetworkBranding 元数据 | 阅读 references/branding.md 并部署自定义品牌样式 |
MFA 页面 URL 中出现 vforcesite | 预期行为,并非 Bug | 平台通过 Force.com Site 域名提供登录和 MFA 页面 |
| 已启用身份验证,但社区没有 MFA | 使用了错误的机制 | 改为通过权限集使用 ForceTwoFactor |
| 用户已启用 MFA,但未收到验证挑战 | 存在活动会话 | 在无痕/隐私浏览器中测试 |
| 已部署权限集,但未强制执行 MFA | 已部署但未分配 | 执行分配步骤——部署不等于分配 |
| 组织中未找到社区用户 | 尚未创建用户或用户尚未自行注册 | 可创建测试社区用户(Account → Contact → User 层级)进行验证。权限集仍已部署,且 networkMemberGroups 已更新——组织已为 MFA 做好准备,用户创建后即可使用。 |
| 新用户未自动受到 MFA 保护 | networkMemberGroups 仅定义站点成员身份,不会向用户分配权限集 | 向需要启用 MFA 的每位用户分配 MFA_Required_For_Community + API_Enabled_For_Community(步骤 4) |
FORBIDDEN: You do not have access to the Apex class named: UIBundleLogin | 站点访客配置文件缺少 Apex 类访问权限 | 执行步骤 3c,授予访客配置文件对所有 UIBundle 登录类的访问权限 |
社区用户无法登录(悄悄重定向或收到 portal user email settings 错误) | 社区配置文件不是网络成员,或电子邮件可送达性未设置为 All Email | 将配置文件添加到 .network-meta.xml 的 <networkMemberGroups> 中并重新部署(步骤 3b)。在 Setup → Email → Deliverability 中确认电子邮件可送达性已设置为 "All Email"。 |
输出预期
在用户项目中生成的文件:
| 文件 | 适用情况 |
|---|---|
permissionsets/MFA_Required_For_Community.permissionset-meta.xml | 始终 |
permissionsets/API_Enabled_For_Community.permissionset-meta.xml | 始终 |
**总结已完成的工作时,切勿声称将权限集添加到
<networkMemberGroups>(或更新网络)会让新用户或自行注册的 用户自动获得 MFA。**事实并非如此——networkMemberGroups仅 定义站点成员身份。只有明确分配了权限集的用户才会强制启用 MFA(步骤 4)。描述网络变更时,应表述为“已将权限集注册为站点 成员”,而不是“自动分配”。
跨技能集成
| 适用情况 | 委托给 |
|---|---|
| 用户只需分配(已部署) | dx-org-permission-set-assign |
| 用户需要部署所有项目元数据 | platform-metadata-deploy |
| 用户想要自定义登录页面 UI | experience-ui-bundle-frontend-generate |
| 用户需要创建新的通用权限集 | platform-permission-set-generate |
| 用户想要使用 IDP/社交登录(与 MFA 不同) | React 站点支持此功能——内置社交登录组件会自动在登录页面显示已关联的 Auth Provider。在 Setup 中创建 Auth Provider,然后通过 experience-ui-bundle-deploy 的社交登录步骤将其关联到 React 站点(在 org-setup.config.json 中配置 socialLogin)——React SSO 管理界面已隐藏,因此需要通过程序关联,而非在 Setup 中点击设置。请参阅 references/social-login.md。 |
参考文件索引
| 文件 | 何时阅读 |
|---|---|
assets/MFA_Required_For_Community.permissionset-meta.xml | 步骤 2 — 将权限集写入项目 |
assets/API_Enabled_For_Community.permissionset-meta.xml | 步骤 2 — 始终部署 |
references/branding.md | 用户想要自定义 MFA/登录页面外观时 |
references/social-login.md | 用户想要在 React 网站上使用 IDP/SSO/社交登录,与 MFA 并用或取代 MFA 时 |
references/setup.md | 步骤 3–5 — 详细的分配、网络成员身份和发布参考 |