更新Facebook客户端密钥后Azure AD B2C自定义策略出现邮箱重复错误
Azure AD B2C自定义策略Facebook登录重复账户问题修复
问题描述
我已在Azure AD B2C自定义策略中更新了新的Facebook Client Id和Secret。当已在ADB2C租户中注册的用户通过Facebook账户登录时,出现了“Email already exists”错误。我希望修改/更新自定义策略,避免Facebook用户在租户中重复创建账户。此前更新Google Application Client Id后可正常运行,原以为相同流程适用于Facebook IdP,但实际并非如此。
当前策略代码片段
以下是我的扩展策略中显示错误信息的代码片段(来自Starter Pack的标准内容):
<ClaimsProvider> <DisplayName>Social accounts</DisplayName> <TechnicalProfiles> <!-- force unique email address - Collect, validate and return the email address as output claim. So, next orchestration step will persist the email to the directory into otherMails attribute --> <TechnicalProfile Id="SelfAsserted-SocialEmail"> <DisplayName>Email signup</DisplayName> <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.SelfAssertedAttributeProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> <Metadata> <Item Key="IpAddressClaimReferenceId">IpAddress</Item> <Item Key="ContentDefinitionReferenceId">api.selfasserted.localaccountsignup</Item> <Item Key="language.button_continue">Continue</Item> </Metadata> <InputClaims> <InputClaim ClaimTypeReferenceId="email" /> </InputClaims> <OutputClaims> <OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="Verified.Email" Required="true" /> </OutputClaims> <UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD" /> </TechnicalProfile> </TechnicalProfiles> </ClaimsProvider> <ClaimsProvider> <DisplayName>Azure Active Directory</DisplayName> <TechnicalProfiles> <!--force unique email address - This technical profile tries to find a local account with provided email address--> <TechnicalProfile Id="AAD-UserReadUsingLocalAccountEmail-NoError"> <Metadata> <Item Key="Operation">Read</Item> <Item Key="RaiseErrorIfClaimsPrincipalDoesNotExist">false</Item> </Metadata> <InputClaims> <InputClaim ClaimTypeReferenceId="email" PartnerClaimType="signInNames" Required="true" /> </InputClaims> <OutputClaims> <!-- Required claims --> <OutputClaim ClaimTypeReferenceId="tempObjectId" PartnerClaimType="objectId" /> </OutputClaims> <IncludeTechnicalProfile ReferenceId="AAD-Common" /> </TechnicalProfile> <!-- force unique email address - This technical profile tries to find a social account with provided email address--> <TechnicalProfile Id="AAD-UserReadUsingSocialEmail-NoError"> <Metadata> <Item Key="Operation">Read</Item> <Item Key="RaiseErrorIfClaimsPrincipalDoesNotExist">false</Item> </Metadata> <InputClaims> <InputClaim ClaimTypeReferenceId="email" PartnerClaimType="signInNames.socialEmailAddress" Required="true" /> </InputClaims> <OutputClaims> <!-- Required claims --> <OutputClaim ClaimTypeReferenceId="tempObjectId" PartnerClaimType="objectId" /> </OutputClaims> <IncludeTechnicalProfile ReferenceId="AAD-Common" /> </TechnicalProfile> </TechnicalProfiles> </ClaimsProvider> <!-- force unique email address - this technical profile displays the message to the user--> <ClaimsProvider> <DisplayName>Self Asserted</DisplayName> <TechnicalProfiles> <TechnicalProfile Id="SelfAsserted-UniqueUserMessage"> <DisplayName>Email unique validation</DisplayName> <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.SelfAssertedAttributeProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> <Metadata> <Item Key="ContentDefinitionReferenceId">api.selfasserted</Item> <!-- hide the continue and cancel buttons --> <Item Key="setting.showContinueButton">false</Item> <Item Key="setting.showCancelButton">false</Item> </Metadata> <!--InputClaimsTransformations> <InputClaimsTransformation ReferenceId="CreateTheUniqueEmailErrorMessage" /> </InputClaimsTransformations--> <InputClaims> <InputClaim ClaimTypeReferenceId="uniqueMailUserMessage" /> </InputClaims> <OutputClaims> <!-- Show the paragraph claim with the message to the user --> <OutputClaim ClaimTypeReferenceId="uniqueMailUserMessage" /> </OutputClaims> <UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" /> </TechnicalProfile> </TechnicalProfiles> </ClaimsProvider>
解决方案思路
问题核心在于:更换Facebook应用后,新的Facebook身份提供商返回的用户标识符(sub声明)与旧应用的不同,B2C会将其视为新用户,但该用户的邮箱已存在于租户中,导致冲突。需要修改策略实现邮箱匹配的社交账户链接,而非强制创建新账户。
具体代码修改步骤
1. 更新Facebook技术配置文件
确保获取用户邮箱并持久化到对应属性:
<ClaimsProvider> <DisplayName>Facebook</DisplayName> <TechnicalProfiles> <TechnicalProfile Id="Facebook-OAUTH"> <Metadata> <Item Key="client_id">你的新Facebook Client Id</Item> <Item Key="scope">email public_profile</Item> <Item Key="ClaimsEndpoint">https://graph.facebook.com/v2.9/me?fields=id,first_name,last_name,email</Item> </Metadata> <OutputClaims> <OutputClaim ClaimTypeReferenceId="socialIdpUserId" PartnerClaimType="id" /> <OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="email" /> <OutputClaim ClaimTypeReferenceId="givenName" PartnerClaimType="first_name" /> <OutputClaim ClaimTypeReferenceId="surname" PartnerClaimType="last_name" /> <OutputClaim ClaimTypeReferenceId="displayName" DefaultValue="{givenName} {surname}" /> <OutputClaim ClaimTypeReferenceId="identityProvider" DefaultValue="facebook.com" /> </OutputClaims> <PersistedClaims> <PersistedClaim ClaimTypeReferenceId="email" PartnerClaimType="signInNames.socialEmailAddress" /> </PersistedClaims> </TechnicalProfile> </TechnicalProfiles> </ClaimsProvider>
2. 调整用户旅程的账户检查与链接逻辑
在社交登录后添加账户查询与分支处理:
<UserJourney Id="SignUpOrSignIn"> <OrchestrationSteps> <!-- 步骤1:选择身份提供商 --> <OrchestrationStep Order="1" Type="CombinedSignInAndSignUp" ContentDefinitionReferenceId="api.signuporsignin"> <ClaimsProviderSelections> <ClaimsProviderSelection TargetClaimsExchangeId="FacebookExchange" /> </ClaimsProviderSelections> </OrchestrationStep> <!-- 步骤2:Facebook登录 --> <OrchestrationStep Order="2" Type="ClaimsExchange"> <ClaimsExchanges> <ClaimsExchange Id="FacebookExchange" TechnicalProfileReferenceId="Facebook-OAUTH" /> </ClaimsExchanges> </OrchestrationStep> <!-- 步骤3:检查邮箱是否存在于本地账户 --> <OrchestrationStep Order="3" Type="ClaimsExchange"> <Preconditions> <Precondition Type="ClaimsExist" ExecuteActionsIf="true"> <Value>tempObjectId</Value> <Action>SkipThisOrchestrationStep</Action> </Precondition> </Preconditions> <ClaimsExchanges> <ClaimsExchange Id="CheckLocalAccount" TechnicalProfileReferenceId="AAD-UserReadUsingLocalAccountEmail-NoError" /> </ClaimsExchanges> </OrchestrationStep> <!-- 步骤4:检查邮箱是否存在于社交账户 --> <OrchestrationStep Order="4" Type="ClaimsExchange"> <Preconditions> <Precondition Type="ClaimsExist" ExecuteActionsIf="true"> <Value>tempObjectId</Value> <Action>SkipThisOrchestrationStep</Action> </Precondition> </Preconditions> <ClaimsExchanges> <ClaimsExchange Id="CheckSocialAccount" TechnicalProfileReferenceId="AAD-UserReadUsingSocialEmail-NoError" /> </ClaimsExchanges> </OrchestrationStep> <!-- 步骤5:链接已有账户 --> <OrchestrationStep Order="5" Type="ClaimsExchange"> <Preconditions> <Precondition Type="ClaimsExist" ExecuteActionsIf="false"> <Value>tempObjectId</Value> <Action>SkipThisOrchestrationStep</Action> </Precondition> </Preconditions> <ClaimsExchanges> <ClaimsExchange Id="LinkAccount" TechnicalProfileReferenceId="AAD-UserLink" /> </ClaimsExchanges> </OrchestrationStep> <!-- 步骤6:创建新账户(如果未找到已有账户) --> <OrchestrationStep Order="6" Type="ClaimsExchange"> <Preconditions> <Precondition Type="ClaimsExist" ExecuteActionsIf="true"> <Value>tempObjectId</Value> <Action>SkipThisOrchestrationStep</Action> </Precondition> </Preconditions> <ClaimsExchanges> <ClaimsExchange Id="CreateSocialAccount" TechnicalProfileReferenceId="AAD-UserWriteUsingAlternativeSecurityId" /> </ClaimsExchanges> </OrchestrationStep> <!-- 后续步骤... --> </OrchestrationSteps> </UserJourney>
3. 确认账户链接技术配置文件存在
确保策略中包含该配置:
<TechnicalProfile Id="AAD-UserLink"> <Metadata> <Item Key="Operation">Link</Item> <Item Key="RaiseErrorIfClaimsPrincipalDoesNotExist">true</Item> </Metadata> <InputClaims> <InputClaim ClaimTypeReferenceId="tempObjectId" PartnerClaimType="objectId" Required="true" /> <InputClaim ClaimTypeReferenceId="alternativeSecurityId" Required="true" /> </InputClaims> <IncludeTechnicalProfile ReferenceId="AAD-Common" /> </TechnicalProfile>
注意事项
- 确保所有技术配置文件的引用ID与你的策略结构一致;
- 测试前备份原有策略,避免配置错误导致服务中断;
- 验证Facebook应用的权限设置,确保
email权限已启用并能正确返回邮箱信息。
内容的提问来源于stack exchange,提问作者Yaroslav Zghurovskyi
相关产品推荐
相关产品推荐

