You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Azure AD B2C自定义策略登录旅程MFA功能定制需求问询

Azure AD B2C自定义策略:邮箱TOTP MFA集成及规则实现

一、核心流程改造:凭证校验后添加邮箱TOTP MFA

基于现有邮箱/ID密码登录流程,需在用户完成凭证校验后插入邮箱TOTP发送与验证环节,具体实现步骤如下:

  • 添加TOTP技术配置
    在自定义策略的<ClaimsProviders>节点下,新增TOTP发送与验证的声明提供方,配置邮箱发送服务(可对接Azure通信服务或自定义SMTP):

    <ClaimsProvider>
      <DisplayName>Email TOTP</DisplayName>
      <TechnicalProfiles>
        <!-- 发送TOTP到用户注册邮箱 -->
        <TechnicalProfile Id="Email-SendTOTP">
          <DisplayName>Send TOTP to Email</DisplayName>
          <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.EmailProtocolProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
          <Metadata>
            <Item Key="SmtpServer">smtp.yourdomain.com</Item>
            <Item Key="SmtpPort">587</Item>
            <Item Key="Username">smtp-account@yourdomain.com</Item>
            <Item Key="Password">smtp-password</Item>
          </Metadata>
          <InputClaims>
            <InputClaim ClaimTypeReferenceId="email" PartnerClaimType="to" />
            <InputClaim ClaimTypeReferenceId="totpCode" PartnerClaimType="subject" />
          </InputClaims>
        </TechnicalProfile>
        <!-- 验证用户输入的TOTP -->
        <TechnicalProfile Id="Email-VerifyTOTP">
          <DisplayName>Verify TOTP</DisplayName>
          <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.TotpProtocolProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
          <Metadata>
            <Item Key="ValidationTimeInMinutes">10</Item>
          </Metadata>
          <InputClaims>
            <InputClaim ClaimTypeReferenceId="totpCode" PartnerClaimType="otp" />
          </InputClaims>
          <OutputClaims>
            <OutputClaim ClaimTypeReferenceId="totpVerified" DefaultValue="true" />
          </OutputClaims>
        </TechnicalProfile>
      </TechnicalProfiles>
    </ClaimsProvider>
    
  • 修改用户旅程流程
    在现有登录用户旅程的OrchestrationSteps中,将TOTP发送与验证步骤插入到凭证校验步骤之后:

    <!-- 步骤N:凭证校验通过后,触发TOTP发送 -->
    <OrchestrationStep Order="N" Type="ClaimsExchange">
      <ClaimsExchanges>
        <ClaimsExchange Id="SendTOTP" TechnicalProfileReferenceId="Email-SendTOTP" />
      </ClaimsExchanges>
    </OrchestrationStep>
    <!-- 步骤N+1:显示TOTP验证界面,收集用户输入并校验 -->
    <OrchestrationStep Order="N+1" Type="ClaimsExchange">
      <ClaimsExchanges>
        <ClaimsExchange Id="VerifyTOTP" TechnicalProfileReferenceId="Email-VerifyTOTP" />
      </ClaimsExchanges>
    </OrchestrationStep>
    
  • 定制TOTP验证界面
    在ContentDefinitions中配置TOTP验证页面,添加“30天内记住我”复选框:

    <ContentDefinition Id="api.selfasserted.verifytotp">
      <LoadUri>~/tenant/templates/AzureBlue/selfAsserted.cshtml</LoadUri>
      <RecoveryUri>~/common/default_page_error.html</RecoveryUri>
      <DataUri>urn:com:microsoft:aad:b2c:elements:selfasserted:1.1.0</DataUri>
      <Metadata>
        <Item Key="DisplayName">Verify Email Code</Item>
      </Metadata>
      <LocalizedResourcesReferences MergeBehavior="Prepend">
        <LocalizedResourcesReference Language="en" LocalizedResourcesReferenceId="api.selfasserted.en" />
      </LocalizedResourcesReferences>
    </ContentDefinition>
    

二、实现“30天记住我跳过MFA”规则

利用Azure AD B2C的持久化会话和自定义声明,实现MFA跳过逻辑:

  • 添加自定义声明
    在<BuildingBlocks><ClaimsSchema>中新增用于标记“记住我”和存储上次MFA时间的声明:

    <ClaimType Id="rememberMe">
      <DisplayName>Remember me for 30 days</DisplayName>
      <DataType>boolean</DataType>
      <UserInputType>Checkbox</UserInputType>
    </ClaimType>
    <ClaimType Id="lastMfaDate">
      <DisplayName>Last MFA Verification Date</DisplayName>
      <DataType>dateTime</DataType>
    </ClaimType>
    
  • 持久化MFA验证记录
    修改Email-VerifyTOTP技术配置,当用户勾选“记住我”时,将当前时间持久化到浏览器会话:

    <OutputClaims>
      <OutputClaim ClaimTypeReferenceId="totpVerified" DefaultValue="true" />
      <OutputClaim ClaimTypeReferenceId="lastMfaDate" PartnerClaimType="persistentLastMfaDate" DefaultValue="{Context:DateTimeNow}" AlwaysUseDefaultValue="true" />
    </OutputClaims>
    <PersistedClaims>
      <PersistedClaim ClaimTypeReferenceId="lastMfaDate" Condition="#{rememberMe} == true" />
    </PersistedClaims>
    
  • 添加MFA跳过判断逻辑
    在用户旅程起始处添加步骤,读取持久化的lastMfaDate并计算时间差,若小于30天则标记跳过MFA:

    <OrchestrationStep Order="1" Type="ClaimsExchange">
      <ClaimsExchanges>
        <ClaimsExchange Id="CheckMfaSkipCondition" TechnicalProfileReferenceId="CheckMfaSkipCondition" />
      </ClaimsExchanges>
    </OrchestrationStep>
    
    <!-- 定义判断技术配置 -->
    <TechnicalProfile Id="CheckMfaSkipCondition">
      <DisplayName>Check if MFA can be skipped</DisplayName>
      <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.ClaimsTransformationProtocolProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
      <OutputClaims>
        <OutputClaim ClaimTypeReferenceId="skipMfa" DefaultValue="false" />
      </OutputClaims>
      <OutputClaimsTransformations>
        <OutputClaimsTransformation ReferenceId="CalculateDaysSinceLastMfa" />
        <OutputClaimsTransformation ReferenceId="SetSkipMfaIfWithin30Days" />
      </OutputClaimsTransformations>
    </TechnicalProfile>
    
    <!-- 声明转换:计算距离上次MFA的天数 -->
    <ClaimsTransformation Id="CalculateDaysSinceLastMfa" TransformationMethod="DateTimeDifferenceInDays">
      <InputClaims>
        <InputClaim ClaimTypeReferenceId="lastMfaDate" TransformationClaimType="startDateTime" />
        <InputClaim ClaimTypeReferenceId="currentDateTime" TransformationClaimType="endDateTime" />
      </InputClaims>
      <OutputClaims>
        <OutputClaim ClaimTypeReferenceId="daysSinceLastMfa" TransformationClaimType="differenceInDays" />
      </OutputClaims>
    </ClaimsTransformation>
    
    <!-- 声明转换:判断是否跳过MFA -->
    <ClaimsTransformation Id="SetSkipMfaIfWithin30Days" TransformationMethod="CompareClaims">
      <InputClaims>
        <InputClaim ClaimTypeReferenceId="daysSinceLastMfa" TransformationClaimType="inputClaim1" />
        <InputClaim ClaimTypeReferenceId="30" TransformationClaimType="inputClaim2" />
      </InputClaims>
      <InputParameters>
        <InputParameter Id="operator" DataType="string" Value="less than" />
        <InputParameter Id="outputClaimIfMatched" DataType="boolean" Value="true" />
      </InputParameters>
      <OutputClaims>
        <OutputClaim ClaimTypeReferenceId="skipMfa" TransformationClaimType="outputClaim" />
      </OutputClaims>
    </ClaimsTransformation>
    
  • 添加条件分支控制MFA执行
    修改TOTP相关步骤的执行条件,仅当skipMfa为false时才执行:

    <OrchestrationStep Order="N" Type="ClaimsExchange" Condition="#{skipMfa} == false">
      <ClaimsExchanges>
        <ClaimsExchange Id="SendTOTP" TechnicalProfileReferenceId="Email-SendTOTP" />
      </ClaimsExchanges>
    </OrchestrationStep>
    

三、实现“异常行为强制MFA”规则

通过集成Azure AD Identity Protection或自定义风险检测,强制触发MFA:

  • 集成Identity Protection风险信号
    在用户读取技术配置中添加风险等级声明,获取用户登录风险信息:

    <ClaimsProvider>
      <DisplayName>AAD IDP</DisplayName>
      <TechnicalProfiles>
        <TechnicalProfile Id="AAD-UserReadUsingEmailAddress">
          <OutputClaims>
            <OutputClaim ClaimTypeReferenceId="riskLevelAggregated" />
          </OutputClaims>
        </TechnicalProfile>
      </TechnicalProfiles>
    </ClaimsProvider>
    
  • 强制MFA的条件判断
    修改MFA步骤的执行条件,若检测到中高风险,则忽略skipMfa声明,强制执行MFA:

    <OrchestrationStep Order="N" Type="ClaimsExchange" Condition="#{skipMfa} == false OR #{riskLevelAggregated} == 'high' OR #{riskLevelAggregated} == 'medium'">
      <ClaimsExchanges>
        <ClaimsExchange Id="SendTOTP" TechnicalProfileReferenceId="Email-SendTOTP" />
      </ClaimsExchanges>
    </OrchestrationStep>
    
  • 自定义风险检测(可选)
    若需自定义风险规则(如异地登录、陌生设备),可调用自定义API获取风险信号,再基于结果强制MFA:

    <TechnicalProfile Id="CustomRiskDetection">
      <DisplayName>Custom Risk Detection API</DisplayName>
      <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.RestfulProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
      <Metadata>
        <Item Key="ServiceUrl">https://your-risk-api.com/detect</Item>
        <Item Key="AuthenticationType">None</Item>
        <Item Key="SendClaimsIn">Body</Item>
      </Metadata>
      <InputClaims>
        <InputClaim ClaimTypeReferenceId="ipAddress" />
        <InputClaim ClaimTypeReferenceId="deviceId" />
      </InputClaims>
      <OutputClaims>
        <OutputClaim ClaimTypeReferenceId="isRisky" />
      </OutputClaims>
    </TechnicalProfile>
    

    对应的用户旅程条件:

    <OrchestrationStep Order="N" Type="ClaimsExchange" Condition="#{skipMfa} == false OR #{isRisky} == 'true'">
      <ClaimsExchanges>
        <ClaimsExchange Id="SendTOTP" TechnicalProfileReferenceId="Email-SendTOTP" />
      </ClaimsExchanges>
    </OrchestrationStep>
    

内容的提问来源于stack exchange,提问作者Vamsi Krishna

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 05:04:51