首页 > 解决方案 > AADB2C 自定义策略 - 本地和社交帐户签名策略,带有拆分电子邮件验证和注册

问题描述

我正在尝试创建具有以下用户旅程的 Azure AD B2C 自定义策略 -

为此,我从示例策略开始 - https://github.com/azure-ad-b2c/samples/tree/master/policies/sign-up-with-social-and-local-account

并从示例策略中添加了 EmailVerification 和 LocalAccountSignUpWithReadOnlyEmail 技术配置文件 - https://github.com/azure-ad-b2c/samples/tree/master/policies/split-email-verification-and-signup

为了触发拆分电子邮件验证和注册流程,我已将 SignUpTarget 设置为 EmailVerification。

我可以看到登录/注册页面,单击注册链接会触发电子邮件验证流程。但是,我不确定如何在电子邮件验证后触发 LocalAccountSignUpWithReadOnlyEmail 技术配置文件。将此作为 ClaimsExchange 编排步骤的一部分添加会在上传我的自定义策略时导致验证错误。

这是我的用户旅程配置的样子 -

<UserJourneys>
    <UserJourney Id="SignUpOrSignIn">
        <OrchestrationSteps>

            <OrchestrationStep Order="1" Type="CombinedSignInAndSignUp" ContentDefinitionReferenceId="api.signuporsignin">
                <ClaimsProviderSelections>
                    <ClaimsProviderSelection TargetClaimsExchangeId="FacebookExchange" />
                    <ClaimsProviderSelection ValidationClaimsExchangeId="LocalAccountSigninEmailExchange" />
                </ClaimsProviderSelections>
                <ClaimsExchanges>
                    <ClaimsExchange Id="LocalAccountSigninEmailExchange" TechnicalProfileReferenceId="SelfAsserted-LocalAccountSignin-Email" />
                </ClaimsExchanges>
            </OrchestrationStep>

            <!-- Check if the user has selected to sign in using one of the social providers -->
            <OrchestrationStep Order="2" Type="ClaimsExchange">
                <Preconditions>
                    <Precondition Type="ClaimsExist" ExecuteActionsIf="true">
                        <Value>objectId</Value>
                        <Action>SkipThisOrchestrationStep</Action>
                    </Precondition>
                </Preconditions>
                <ClaimsExchanges>
                    <ClaimsExchange Id="FacebookExchange" TechnicalProfileReferenceId="Facebook-OAUTH" />
                    <ClaimsExchange Id="EmailVerification" TechnicalProfileReferenceId="EmailVerification" />
                </ClaimsExchanges>
            </OrchestrationStep>

            <OrchestrationStep Order="3" Type="ClaimsExchange">
                <ClaimsExchanges>
                    <ClaimsExchange Id="LocalAccountSignUpWithReadOnlyEmail" TechnicalProfileReferenceId="LocalAccountSignUpWithReadOnlyEmail" />
                </ClaimsExchanges>
            </OrchestrationStep>

            <!-- For social IDP authentication, attempt to find the user account in the directory. -->
            <OrchestrationStep Order="4" Type="ClaimsExchange">
                <Preconditions>
                    <Precondition Type="ClaimEquals" ExecuteActionsIf="true">
                        <Value>authenticationSource</Value>
                        <Value>localAccountAuthentication</Value>
                        <Action>SkipThisOrchestrationStep</Action>
                    </Precondition>
                </Preconditions>
                <ClaimsExchanges>
                    <ClaimsExchange Id="AADUserReadUsingAlternativeSecurityId" TechnicalProfileReferenceId="AAD-UserReadUsingAlternativeSecurityId-NoError" />
                </ClaimsExchanges>
            </OrchestrationStep>

            <!-- Show self-asserted page only if the directory does not have the user account already (i.e. we do not have an objectId). 
      This can only happen when authentication happened using a social IDP. If local account was created or authentication done
      using ESTS in step 2, then an user account must exist in the directory by this time. -->
            <OrchestrationStep Order="5" Type="ClaimsExchange">
                <Preconditions>
                    <Precondition Type="ClaimsExist" ExecuteActionsIf="true">
                        <Value>objectId</Value>
                        <Action>SkipThisOrchestrationStep</Action>
                    </Precondition>
                </Preconditions>
                <ClaimsExchanges>
                    <ClaimsExchange Id="SelfAsserted-Social" TechnicalProfileReferenceId="SelfAsserted-Social" />
                </ClaimsExchanges>
            </OrchestrationStep>

            <!-- This step reads any user attributes that we may not have received when authenticating using ESTS so they can be sent 
      in the token. -->
            <OrchestrationStep Order="6" Type="ClaimsExchange">
                <Preconditions>
                    <Precondition Type="ClaimEquals" ExecuteActionsIf="true">
                        <Value>authenticationSource</Value>
                        <Value>socialIdpAuthentication</Value>
                        <Action>SkipThisOrchestrationStep</Action>
                    </Precondition>
                </Preconditions>
                <ClaimsExchanges>
                    <ClaimsExchange Id="AADUserReadWithObjectId" TechnicalProfileReferenceId="AAD-UserReadUsingObjectId" />
                </ClaimsExchanges>
            </OrchestrationStep>
            <!-- The previous step (SelfAsserted-Social) could have been skipped if there were no attributes to collect 
         from the user. So, in that case, create the user in the directory if one does not already exist 
         (verified using objectId which would be set from the last step if account was created in the directory. -->
            <OrchestrationStep Order="7" Type="ClaimsExchange">
                <Preconditions>
                    <Precondition Type="ClaimsExist" ExecuteActionsIf="true">
                        <Value>objectId</Value>
                        <Action>SkipThisOrchestrationStep</Action>
                    </Precondition>
                </Preconditions>
                <ClaimsExchanges>
                    <ClaimsExchange Id="AADUserWrite" TechnicalProfileReferenceId="AAD-UserWriteUsingAlternativeSecurityId" />
                </ClaimsExchanges>
            </OrchestrationStep>

            <OrchestrationStep Order="8" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />

        </OrchestrationSteps>
        <ClientDefinition ReferenceId="DefaultWeb" />
    </UserJourney>
</UserJourneys>

这是技术配置文件的样子 -

<ClaimsProviders>
    <ClaimsProvider>
        <DisplayName>Email Verification</DisplayName>
        <TechnicalProfiles>
            <!--Sample: Email verification only-->
            <TechnicalProfile Id="EmailVerification">
                <DisplayName>Initiate Email Address Verification For Local Account</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.localaccountsignup</Item>
                    <Item Key="language.button_continue">Continue</Item>
                </Metadata>
                <CryptographicKeys>
                    <Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" />
                </CryptographicKeys>
                <IncludeInSso>false</IncludeInSso>
                <InputClaims>
                    <InputClaim ClaimTypeReferenceId="email" />
                </InputClaims>
                <OutputClaims>
                    <OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="Verified.Email" Required="true" />
                </OutputClaims>
            </TechnicalProfile>

            <!-- This technical profile uses a validation technical profile to authenticate the user. -->
            <TechnicalProfile Id="SelfAsserted-LocalAccountSignin-Email">
                <DisplayName>Local Account Signin</DisplayName>
                <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.SelfAssertedAttributeProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
                <Metadata>
                    <Item Key="SignUpTarget">EmailVerification</Item>
                    <Item Key="setting.operatingMode">Email</Item>
                    <Item Key="ContentDefinitionReferenceId">api.selfasserted</Item>
                </Metadata>
                <IncludeInSso>false</IncludeInSso>
                <InputClaims>
                    <InputClaim ClaimTypeReferenceId="signInName" />
                </InputClaims>
                <OutputClaims>
                    <OutputClaim ClaimTypeReferenceId="signInName" Required="true" />
                    <OutputClaim ClaimTypeReferenceId="password" Required="true" />
                    <OutputClaim ClaimTypeReferenceId="objectId" />
                    <OutputClaim ClaimTypeReferenceId="authenticationSource" />
                </OutputClaims>
                <ValidationTechnicalProfiles>
                    <ValidationTechnicalProfile ReferenceId="login-NonInteractive" />
                </ValidationTechnicalProfiles>
                <UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD" />
            </TechnicalProfile>
        </TechnicalProfiles>
    </ClaimsProvider>

    <ClaimsProvider>
        <DisplayName>Local Account</DisplayName>
        <TechnicalProfiles>
            <!--Sample: Sign-up self-asserted technical profile without Email verification-->
            <TechnicalProfile Id="LocalAccountSignUpWithReadOnlyEmail">
                <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.localaccountsignup</Item>
                    <Item Key="language.button_continue">Create</Item>
                    <!-- Sample: Remove sign-up email verification -->
                    <Item Key="EnforceEmailVerification">False</Item>
                </Metadata>
                <InputClaimsTransformations>
                    <InputClaimsTransformation ReferenceId="CreateReadonlyEmailClaim" />
                </InputClaimsTransformations>
                <InputClaims>
                    <!--Sample: Set input the ReadOnlyEmail claim type to prefilled the email address-->
                    <InputClaim ClaimTypeReferenceId="readOnlyEmail" />
                </InputClaims>
                <OutputClaims>
                    <OutputClaim ClaimTypeReferenceId="objectId" />
                    <!-- Sample: Display the ReadOnlyEmail claim type (instead of email claim type)-->
                    <OutputClaim ClaimTypeReferenceId="readOnlyEmail" Required="true" />
                    <OutputClaim ClaimTypeReferenceId="newPassword" Required="true" />
                    <OutputClaim ClaimTypeReferenceId="reenterPassword" Required="true" />
                    <OutputClaim ClaimTypeReferenceId="executed-SelfAsserted-Input" DefaultValue="true" />
                    <OutputClaim ClaimTypeReferenceId="authenticationSource" />
                    <OutputClaim ClaimTypeReferenceId="newUser" />

                    <!-- Optional claims, to be collected from the user -->
                    <!--OutputClaim ClaimTypeReferenceId="displayName" /-->
                    <OutputClaim ClaimTypeReferenceId="givenName" />
                    <OutputClaim ClaimTypeReferenceId="surName" />
                </OutputClaims>
                <ValidationTechnicalProfiles>
                    <ValidationTechnicalProfile ReferenceId="AAD-UserWriteUsingLogonEmail" />
                </ValidationTechnicalProfiles>
                <!-- Sample: Disable session management for sign-up page -->
                <UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
            </TechnicalProfile>
        </TechnicalProfiles>
    </ClaimsProvider>
</ClaimsProviders>

这是我尝试上传策略时遇到的错误 -

Validation failed: 4 validation error(s) found in policy "B2C_1A_CUSTOM_SIGNUP_SIGNIN" of tenant "testtenant.onmicrosoft.com".User journey "SignUpOrSignIn" in policy "B2C_1A_custom_signup_signin" of tenant "testtenant.onmicrosoft.com" has step 3 with 2 claims exchanges. It must be preceded by a claims provider selection in order to determine which claims exchange can be used.User journey "SignUpOrSignIn" in policy "B2C_1A_custom_signup_signin" of tenant "testtenant.onmicrosoft.com" has step 4 with 2 claims exchanges. It must be preceded by a claims provider selection in order to determine which claims exchange can be used.User journey "SignUpOrSignIn" in policy "B2C_1A_custom_signup_signin" of tenant "testtenant.onmicrosoft.com" has step 5 with 2 claims exchanges. It must be preceded by a claims provider selection in order to determine which claims exchange can be used.User journey "SignUpOrSignIn" in policy "B2C_1A_custom_signup_signin" of tenant "testtenant.onmicrosoft.com" has step 6 with 2 claims exchanges. It must be preceded by a claims provider selection in order to determine which claims exchange can be used.

在这里寻找一些建议...

标签: azureazure-active-directoryazure-ad-b2c

解决方案


您收到此错误的原因是您可能在 2 个文件中编写了用户旅程ID SignUpOrSignIn基本/扩展回复方政策。

如果步骤计数和ClaimsExchange ID是唯一的,那么它将接受,否则它将视为 2 个不同的 ClaimsExchange,并且在上传 RP 策略时会发生错误。请确保不要重复用户旅程,只保留一份用户旅程步骤的副本,或者如果您想扩展旅程步骤,然后添加这些步骤。例如:在基本策略中,您总共有 5 个步骤,然后在扩展程序或 RP 中,您可以从第 5 步开始添加新的 ClaimsExchange,最后一步将是 JwtIssuer/SamlIssuer。


推荐阅读