Azure AD B2C 自定义策略同时支持电子邮件、电话和 TOTP 选项

问题描述 投票:0回答:1

我正在使用 Azure AD B2c 自定义策略来实现以下目的。我想提供电子邮件、电话和身份验证器应用程序 (TOTP) MFA 选项。用户可以选择这 3 个选项中的任何一个,AD B2C 用户流程应根据所选的 MFA 选择进行工作。

我们有来自以下示例的 PhoneOrEMail XML 配置:

https://github.com/azure-ad-b2c/samples/blob/master/policies/mfa-email-or-phone/policy/SignUpOrSignin_PhoneOrEmailMFA.xml

我们有来自以下示例的 TOTP XML 配置:

https://github.com/azure-ad-b2c/samples/blob/master/policies/totp/policy/TrustFrameworkExtensions_TOTP.xml

我尝试通过为“extension_mfaByPhoneOrEmail”声明类型添加附加选项,也在 SignUpOrSignin_PhoneOrEmailMFA.xml 文件中添加 TOTP 配置,如下所示

<ClaimType Id="extension_mfaByPhoneOrEmail">
        <DisplayName>Please select your preferred MFA method</DisplayName>
        <DataType>string</DataType>
        <UserInputType>RadioSingleSelect</UserInputType>
        <Restriction>
          <Enumeration Text="Phone" Value="phone" SelectByDefault="true" />
          <Enumeration Text="Email " Value="email" SelectByDefault="false" />
          <Enumeration Text="Authenticator App " Value="TOTP" SelectByDefault="false" />
        </Restriction>
</ClaimType>

添加此后,我修改了用户旅程,如下所示:

<UserJourney Id="SignUpOrSignInMFAOption" DefaultCpimIssuerTechnicalProfileReferenceId="JwtIssuer">
      <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 -->
        <!-- ContentDefinitionReferenceId="api.signin" -->
        <OrchestrationStep Order="2" Type="ClaimsExchange">
          <Preconditions>
            <Precondition Type="ClaimsExist" ExecuteActionsIf="true">
              <Value>objectId</Value>
              <Action>SkipThisOrchestrationStep</Action>
            </Precondition>
            <!-- New precondition to check for TOTP selection -->
            <Precondition Type="ClaimEquals" ExecuteActionsIf="true">
              <Value>extension_mfaByPhoneOrEmail</Value>
              <Value>TOTP</Value>
              <Action>SkipThisOrchestrationStep</Action>
            </Precondition>
          </Preconditions>
          <ClaimsExchanges>
            <!-- <ClaimsExchange Id="FacebookExchange" TechnicalProfileReferenceId="Facebook-OAUTH"/> -->
            <ClaimsExchange Id="SignUpWithLogonEmailExchange" TechnicalProfileReferenceId="LocalAccountSignUpWithLogonEmail" />
            <!-- <ClaimsExchange Id="TOTPExchange" TechnicalProfileReferenceId="TOTP-Technical-Profile" /> -->
          </ClaimsExchanges>
        </OrchestrationStep>
        <!-- For social IDP authentication, attempt to find the user account in the directory. -->
        <!-- <OrchestrationStep Order="3" 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="4" 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="3" 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="4" 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> -->
        <!--Sample: If uses is enrolled for MFA, ask the user to select the preferred method-->
        <OrchestrationStep Order="4" Type="ClaimsExchange">
          <Preconditions>
            <Precondition Type="ClaimsExist" ExecuteActionsIf="true">
              <Value>extension_mfaByPhoneOrEmail</Value>
              <Action>SkipThisOrchestrationStep</Action>
            </Precondition>
            <Precondition Type="ClaimEquals" ExecuteActionsIf="true">
              <Value>extension_mfaByPhoneOrEmail</Value>
              <Value>TOTP</Value>
              <Action>SkipThisOrchestrationStep</Action>
            </Precondition>
          </Preconditions>
          <ClaimsExchanges>
            <ClaimsExchange Id="SelfAsserted-Select-MFA-Method" TechnicalProfileReferenceId="SelfAsserted-Select-MFA-Method" />
          </ClaimsExchanges>
        </OrchestrationStep>
        <!-- Throw error if control was bypassed -->
        <OrchestrationStep Order="5" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="Return-MFA-Method-Incorrect-Error">
          <Preconditions>
            <Precondition Type="ClaimEquals" ExecuteActionsIf="true">
              <Value>extension_mfaByPhoneOrEmail</Value>
              <Value>email</Value>
              <Action>SkipThisOrchestrationStep</Action>
            </Precondition>
            <Precondition Type="ClaimEquals" ExecuteActionsIf="true">
              <Value>extension_mfaByPhoneOrEmail</Value>
              <Value>phone</Value>
              <Action>SkipThisOrchestrationStep</Action>
            </Precondition>
            <Precondition Type="ClaimEquals" ExecuteActionsIf="true">
              <Value>extension_mfaByPhoneOrEmail</Value>
              <Value>TOTP</Value>
              <Action>SkipThisOrchestrationStep</Action>
            </Precondition>
          </Preconditions>
        </OrchestrationStep>
        <!-- Phone verification: If MFA is not required, the next three steps (#5-#7) should be removed.
             This step checks whether there's a phone number on record,  for the user. If found, then the user is challenged to verify it. -->
        <OrchestrationStep Order="6" Type="ClaimsExchange">
          <Preconditions>
            <Precondition Type="ClaimsExist" ExecuteActionsIf="true">
              <Value>isActiveMFASession</Value>
              <Action>SkipThisOrchestrationStep</Action>
            </Precondition>
            <!--Sample: If the preferred MFA method is not 'phone' skip this orchestration step-->
            <Precondition Type="ClaimEquals" ExecuteActionsIf="false">
              <Value>extension_mfaByPhoneOrEmail</Value>
              <Value>phone</Value>
              <Action>SkipThisOrchestrationStep</Action>
            </Precondition>
            <Precondition Type="ClaimEquals" ExecuteActionsIf="true">
              <Value>extension_mfaByPhoneOrEmail</Value>
              <Value>TOTP</Value>
              <Action>SkipThisOrchestrationStep</Action>
            </Precondition>
          </Preconditions>
          <ClaimsExchanges>
            <ClaimsExchange Id="PhoneFactor-Verify" TechnicalProfileReferenceId="PhoneFactor-InputOrVerify" />
          </ClaimsExchanges>
        </OrchestrationStep>
        <!-- Save MFA phone number: The precondition verifies whether the user provided a new number in the 
             previous step. If so, then the phone number is stored in the directory for future authentication 
             requests. -->
        <OrchestrationStep Order="7" Type="ClaimsExchange">
          <Preconditions>
            <Precondition Type="ClaimsExist" ExecuteActionsIf="false">
              <Value>newPhoneNumberEntered</Value>
              <Action>SkipThisOrchestrationStep</Action>
            </Precondition>
            <Precondition Type="ClaimEquals" ExecuteActionsIf="true">
              <Value>extension_mfaByPhoneOrEmail</Value>
              <Value>TOTP</Value>
              <Action>SkipThisOrchestrationStep</Action>
            </Precondition>
          </Preconditions>
          <ClaimsExchanges>
            <ClaimsExchange Id="AADUserWriteWithObjectId" TechnicalProfileReferenceId="AAD-UserWritePhoneNumberUsingObjectId" />
          </ClaimsExchanges>
        </OrchestrationStep>
        <!--Sample: MFA with email-->
        <OrchestrationStep Order="8" Type="ClaimsExchange">
          <Preconditions>
            <Precondition Type="ClaimEquals" ExecuteActionsIf="false">
              <Value>extension_mfaByPhoneOrEmail</Value>
              <Value>email</Value>
              <Action>SkipThisOrchestrationStep</Action>
            </Precondition>
            <Precondition Type="ClaimEquals" ExecuteActionsIf="true">
              <Value>extension_mfaByPhoneOrEmail</Value>
              <Value>TOTP</Value>
              <Action>SkipThisOrchestrationStep</Action>
            </Precondition>
          </Preconditions>
          <ClaimsExchanges>
            <ClaimsExchange Id="Email-Verify" TechnicalProfileReferenceId="EmailVerifyOnSignIn" />
          </ClaimsExchanges>
        </OrchestrationStep>
        <OrchestrationStep Order="9" Type="InvokeSubJourney">
          <Preconditions>
            <Precondition Type="ClaimEquals" ExecuteActionsIf="false">
              <Value>extension_mfaByPhoneOrEmail</Value>
              <Value>TOTP</Value>
              <Action>SkipThisOrchestrationStep</Action>
            </Precondition>
          </Preconditions>
          <JourneyList>
            <Candidate SubJourneyReferenceId="SignUpOrSignInTOTP" />
          </JourneyList>
        </OrchestrationStep>
        <OrchestrationStep Order="10" Type="ClaimsExchange">
          <Preconditions>
            <Precondition Type="ClaimsExist" ExecuteActionsIf="false">
              <Value>newUser</Value>
              <Action>SkipThisOrchestrationStep</Action>
            </Precondition>
            <Precondition Type="ClaimEquals" ExecuteActionsIf="true">
              <Value>newUser</Value>
              <Value>false</Value>
              <Action>SkipThisOrchestrationStep</Action>
            </Precondition>
          </Preconditions>
          <ClaimsExchanges>
            <ClaimsExchange Id="TrackUserSignUp" TechnicalProfileReferenceId="AzureInsights-UserSignup" />
          </ClaimsExchanges>
        </OrchestrationStep>
        <OrchestrationStep Order="11" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
        <!-- Handles the user clicking the sign up link in the local account sign in page -->
        <OrchestrationStep Order="12" Type="ClaimsExchange">
          <ClaimsExchanges>
            <ClaimsExchange Id="TrackSignInComplete" TechnicalProfileReferenceId="AzureInsights-SignInComplete" />
          </ClaimsExchanges>
        </OrchestrationStep>
      </OrchestrationSteps>
      <ClientDefinition ReferenceId="DefaultWeb" />
    </UserJourney>

我正在通过 SubJourney 从编排步骤 9 调用 TOTP 流。

我能够成功地将文件上传到 azure ad b2c,能够使用电子邮件、电话和身份验证器应用程序成功注册和登录。

但是,遇到这样的问题:即使在初始设置之后,身份验证器应用程序选项仍会出现 MFA 选择屏幕

我想隐藏身份验证器应用程序的 MFA 屏幕,也类似于电话和电子邮件帖子初始选择。它不应再次显示选择屏幕。

我知道这可能是 extension_mfaByPhoneOrEmail 声明 TOTP 选项选择的持久性。

需要帮助来解决此问题。

azure azure-ad-b2c azure-ad-b2c-custom-policy
1个回答
0
投票

基本上,TOTP Flow 使用以下 numberOfAvailableDevices,只有在用户 TOTP 成功注册后才有价值。因此,我已添加此检查作为编排步骤的一部分,并且能够跳过电话和电子邮件编排步骤并能够完成用户流程。

以下是编排步骤

 <OrchestrationStep Order="4" Type="ClaimsExchange">
      <Preconditions>
        <Precondition Type="ClaimEquals" ExecuteActionsIf="true">
          <Value>newUser</Value>
          <Value>True</Value>
          <Action>SkipThisOrchestrationStep</Action>
        </Precondition>
      </Preconditions>
      <ClaimsExchanges>
        <ClaimsExchange Id="GetAvailableDevicesForUser" TechnicalProfileReferenceId="AzureMfa-GetAvailableDevices" />
      </ClaimsExchanges>
    </OrchestrationStep>

AzureMfa-GetAvailableDevices 将具有以下配置。

<TechnicalProfile Id="AzureMfa-GetAvailableDevices">
      <DisplayName>Get Available Devices</DisplayName>
      <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.AzureMfaProtocolProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
      <Metadata>
        <Item Key="Operation">GetAvailableDevices</Item>
      </Metadata>
      <InputClaims>
        <InputClaim ClaimTypeReferenceId="userPrincipalName" />
      </InputClaims>
      <OutputClaims>
        <OutputClaim ClaimTypeReferenceId="numberOfAvailableDevices" />
      </OutputClaims>
    </TechnicalProfile>

这将确保登录用户的 numberOfAvailableDevices>0,我们可以根据我们的编排步骤进行必要的更改。

© www.soinside.com 2019 - 2024. All rights reserved.