基于区域的配置的 Azure Active Directory B2C 全局标识框架概念证明

以下部分介绍如何为基于区域的业务流程创建概念证明实现。 可在此处找到已完成的 Azure Active Directory B2C (Azure AD B2C) 自定义策略。

基于区域的方法

每个区域 Azure AD B2C 租户都需要一个 Azure AD B2C 自定义策略,其中包含以下功能:

注册旅程:

  • 显示一个屏幕以收集用户的用户名、密码和任何其他属性
  • 如果该用户已存在,则通过查询用户-区域映射表来防止注册
  • 将用户配置文件写入本地租户
  • 将用户的用户名-区域映射写入映射表
  • 向应用程序颁发令牌

登录旅程:

  • 显示用户名和密码屏幕
  • 执行用户名查找并返回其区域
  • 执行本地凭据验证或跨租户凭据验证
  • 从本地租户或通过跨租户调用读取用户配置文件
  • 向应用程序颁发令牌

密码重置旅程:

  • 显示一个屏幕以通过电子邮件 OTP 验证用户电子邮件
  • 执行用户名查找并返回其区域
  • 显示一个屏幕以捕获新密码
  • 将新密码写入本地租户或通过跨租户调用写入
  • 向应用程序颁发令牌

以下方块图展示了概念证明。 本指南将演示如何配置 Azure AD B2C 租户。 本指南不包括外部 API 层和异地分布式查找表。

屏幕截图显示了基于区域的方法的方块图

先决条件

  1. 根据企业需要支持的每个区域创建一个租户。 至少需要为此概念证明创建两个租户。

  2. 将自定义策略部署到租户中。

准备存储层

需要准备一个存储层,它可以存储用户的电子邮件、objectId 和区域。 这样就可以跟踪和查询用户注册的位置。 可以使用 Azure 存储表保存此数据。

准备 API 层

有多个 API 用作概念证明的一部分,以演示基于区域的方法。

验证用户是否已存在

在注册期间使用一个 API 来确定用户是否已存在于任何区域中。

请求将如下所示:

POST /doesUserExistInLookupTable HTTP/1.1
Host: yourapi.com
Content-Type: application/json

{
  email: bob@contoso.com
}

  • 如果用户不存在,则响应应该是 HTTP 200。

  • 如果用户存在,则响应应该是 HTTP 409。

记录用户区域映射

在注册期间使用一个 API 来记录用户注册区域。

请求将如下所示:

POST /userToRegionLookup HTTP/1.1
Host: yourapi.com
Authorization Bearer: <token>
Content-Type: application/json

{
  "email": "bob@contoso.com"
}

  • 如果用户存在,则响应应该是 HTTP 200。

  • 如果用户存在,则响应应该是 HTTP 409。

返回用户所在的区域

在登录期间使用一个 API 来确定用户是在哪个区域注册的。 它会指示是否需要执行跨租户身份验证。

请求将如下所示:

POST /userToRegionLookup HTTP/1.1
Host: yourapi.com
Authorization Bearer: <token>
Content-Type: application/json

{
  "email": "bob@contoso.com"
}

响应应该是 HTTP 200 以及用户注册区域和 objectId。

{
  "objectId": "aaaaaaaa-0000-1111-2222-bbbbbbbbbbbb",
  "region": "APAC"  
}

如果用户不存在或遇到错误,API 应以 HTTP 409 做出响应。

跨租户写入密码

在密码重置流期间使用一个 API 将用户新密码写入他们重置密码所在的不同区域。

请求将如下所示:

POST /writePasswordCrossTenant HTTP/1.1
Host: yourapi.com
Authorization Bearer: <token>
Content-Type: application/json

{
  "objectId": "aaaaaaaa-0000-1111-2222-bbbbbbbbbbbb",
  "password": "some!strong123STRING"
}

如果过程成功,则响应应该是 HTTP 200;如果出现错误,则响应应该是 HTTP 409。

基于区域的 Azure AD B2C 配置

以下部分准备 Azure AD B2C 租户以跟踪用户注册的区域,并在必要时执行跨租户身份验证或密码重置。

注册自定义策略配置

在注册期间,我们必须确保检查用户不存在于任何其他租户中,并将用户的用户-区域映射写入外部表。

如下所示修改 Azure AD B2C 初学者包中的 LocalAccountSignUpWithLogonEmail 技术配置文件:

<TechnicalProfile Id="LocalAccountSignUpWithLogonEmail">
...
  <ValidationTechnicalProfiles>            
    <ValidationTechnicalProfile ReferenceId="REST-getTokenforExternalApiCalls" />
    <ValidationTechnicalProfile ReferenceId="REST-doesUserExistInLookupTable" />        
    <ValidationTechnicalProfile ReferenceId="AAD-UserWriteUsingLogonEmail" />
    <ValidationTechnicalProfile ReferenceId="REST-writeUserToRegionMapping" />
  </ValidationTechnicalProfiles>
  <UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD" />
</TechnicalProfile>

ValidationTechnicalProfiles 将执行以下逻辑:

  1. 获取令牌以使用 REST-getTokenforExternalApiCalls 技术配置文件调用受保护的 API 终结点。

  2. 通过安全外部 REST API 终结点验证用户是否已存在于用户-区域映射中:

    • 在所有注册之前进行此 API 调用;为了符合运行时间要求,有必要确保此 API 具有适当的负载均衡、复原能力和故障转移机制。

    • 通过外部 REST API 查询用户-区域映射的技术配置文件示例如下:

      <TechnicalProfile Id="REST-doesUserExistInLookupTable ">
      <DisplayName>User to Region lookup</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://myApi.com/doesUserExistInLookupTable</Item>
        <Item Key="AuthenticationType">Bearer</Item>
        <Item Key="UseClaimAsBearerToken">ext_Api_bearerToken</Item>
        <Item Key="SendClaimsIn">Body</Item>
        <Item Key="AllowInsecureAuthInProduction">false</Item>
      </Metadata>
      <InputClaims>
        <InputClaim ClaimTypeReferenceId="ext_Api_bearerToken" />
        <InputClaim ClaimTypeReferenceId="signInName" PartnerClaimType="email" />
      </InputClaims>
      <UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
      </TechnicalProfile>
      
    • 如果用户存在,此 API 应以 HTTP 409 做出响应,并在屏幕上显示相应的错误消息。 否则,如果用户不存在,则以 HTTP 200 做出响应。

  3. 通过安全外部 REST API 终结点写入用户-区域映射

    • 在所有注册之前进行此 API 调用;为了符合运行时间要求,有必要确保此 API 具有适当的负载均衡、复原能力和故障转移机制。

    • 通过外部 REST API 写入用户-区域映射的技术配置文件示例如下:

    <TechnicalProfile Id="REST-writeUserToRegionMapping">
    <DisplayName>User to Region lookup</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://myApi.com/writeUserToRegionMapping</Item>
      <Item Key="AuthenticationType">Bearer</Item>
      <Item Key="UseClaimAsBearerToken">ext_Api_bearerToken</Item>
      <Item Key="SendClaimsIn">Body</Item>
      <Item Key="AllowInsecureAuthInProduction">false</Item>
    </Metadata>
    <InputClaims>
      <InputClaim ClaimTypeReferenceId="ext_Api_bearerToken" />
      <InputClaim ClaimTypeReferenceId="signInName" PartnerClaimType="email" />
      <InputClaim ClaimTypeReferenceId="region" DefaultValue="EMEA" />
      <InputClaim ClaimTypeReferenceId="objectId" />
    </InputClaims>
    <UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
    </TechnicalProfile>
    ``` 
    
    

登录自定义策略配置

在登录期间,我们必须确定用户配置文件位置,并根据其配置文件所在的 Azure AD B2C 租户对其进行身份验证。

修改 Azure AD B2C 初学者包中的 SelfAsserted-LocalAccountSignin-Email 技术配置文件以执行用户区域查找,并在用户来自与他们访问的租户不同的区域时执行跨租户身份验证。 将 ValidationTechnicalProfiles 更新为:

<TechnicalProfile Id="SelfAsserted-LocalAccountSignin-Email">
...
  <ValidationTechnicalProfiles>
    <ValidationTechnicalProfile ReferenceId="REST-getTokenforExternalApiCalls" />
    <ValidationTechnicalProfile ReferenceId="REST-regionLookup" />
    <ValidationTechnicalProfile ReferenceId="login-NonInteractive">
      <Preconditions>
        <Precondition Type="ClaimEquals" ExecuteActionsIf="false">
          <Value>user_region</Value>
          <Value>EMEA</Value>
          <Action>SkipThisValidationTechnicalProfile</Action>
        </Precondition>
      </Preconditions>
     <ValidationTechnicalProfile ReferenceId="REST-login-NonInteractive-APAC">
      <Preconditions>
        <Precondition Type="ClaimEquals" ExecuteActionsIf="false">
          <Value>user_region</Value>
          <Value>APAC</Value>
          <Action>SkipThisValidationTechnicalProfile</Action>
        </Precondition>
      </Preconditions>
    </ValidationTechnicalProfile>
    <ValidationTechnicalProfile ReferenceId="REST-fetchUserProfile-APAC">
      <Preconditions>
        <Precondition Type="ClaimEquals" ExecuteActionsIf="false">
          <Value>user_region</Value>
          <Value>APAC</Value>
          <Action>SkipThisValidationTechnicalProfile</Action>
        </Precondition>
      </Preconditions>
    </ValidationTechnicalProfile>
  </ValidationTechnicalProfiles>
  <UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD" />
</TechnicalProfile>

当用户提交其凭据时,ValidationTechnicalProfiles 将执行以下逻辑:

  1. 获取令牌以使用 REST-getTokenforExternalApiCalls 技术配置文件调用受保护的 API 终结点。

  2. 通过安全外部 REST API 终结点查找用户-区域映射

    • 在所有注册之前进行此 API 调用;为了符合运行时间要求,有必要确保此 API 具有适当的负载均衡、复原能力和故障转移机制。

    • 通过外部 REST API 查询用户-区域映射的技术配置文件示例如下:

      <TechnicalProfile Id="REST-regionLookup">
        <DisplayName>User to Region lookup</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://myApi.com/userToRegionLookup</Item>
          <Item Key="AuthenticationType">Bearer</Item>
          <Item Key="UseClaimAsBearerToken">ext_Api_bearerToken</Item>
          <Item Key="SendClaimsIn">Body</Item>
          <Item Key="AllowInsecureAuthInProduction">false</Item>
        </Metadata>
        <InputClaims>
          <InputClaim ClaimTypeReferenceId="ext_Api_bearerToken" />
          <InputClaim ClaimTypeReferenceId="signInName" PartnerClaimType="email" />
        </InputClaims>
        <OutputClaims>
          <OutputClaim ClaimTypeReferenceId="user_region" PartnerClaimType="region" />
          <OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="objectId" />
        </OutputClaims>
        <UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
      </TechnicalProfile>
      
  3. 通过 login-NonInteractive 技术配置文件为在此租户中注册的用户执行本地帐户身份验证。 这是 Azure AD B2C 初学者包中的默认技术配置文件。

  4. 通过 REST-login-NonInteractive-[region] 技术配置文件按条件为每个相应区域执行跨租户身份验证。

    • 这还会从用户主租户获取 MS Graph API 令牌。 在每个具有 MS Graph API 权限的区域租户中完成“原生应用”应用程序注册,以获取以下委托权限:user.read

    • 通过外部 REST API 执行用户-区域映射的技术配置文件示例如下:

      <TechnicalProfile Id="REST-login-NonInteractive-APAC">
        <DisplayName>non interactive authentication to APAC</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://login.partner.microsoftonline.cn/yourAPACb2ctenant.partner.onmschina.cn/oauth2/v2.0/token</Item>
          <Item Key="AuthenticationType">None</Item>
          <Item Key="SendClaimsIn">Form</Item>
          <Item Key="AllowInsecureAuthInProduction">true</Item>
        </Metadata>
        <InputClaims>
          <InputClaim ClaimTypeReferenceId="apac_client_id" PartnerClaimType="client_id" DefaultValue="00001111-aaaa-2222-bbbb-3333cccc4444" />
          <InputClaim ClaimTypeReferenceId="ropc_grant_type" PartnerClaimType="grant_type" DefaultValue="password" />
          <InputClaim ClaimTypeReferenceId="signInName" PartnerClaimType="username" />
          <InputClaim ClaimTypeReferenceId="password" />
          <InputClaim ClaimTypeReferenceId="scope" DefaultValue="https://microsoftgraph.chinacloudapi.cn/.default" AlwaysUseDefaultValue="true" />
          <InputClaim ClaimTypeReferenceId="nca" PartnerClaimType="nca" DefaultValue="1" />
        </InputClaims>
        <OutputClaims>
          <OutputClaim ClaimTypeReferenceId="ext_Api_bearerToken" PartnerClaimType="access_token" />
        </OutputClaims>
        <UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
      </TechnicalProfile>
      
    • ServiceUrl 中的 <yourb2ctenant> 替换为需要作为身份验证目标的租户。

    • 使用应用程序注册 ApplicationIdapac_client_id 输入声明填充 DefaultValue

  5. 使用跨租户 REST API 调用通过 REST-fetchUserProfile-[region] 技术配置文件按条件提取每个相应区域的用户配置文件。

    • 通过 MS Graph API 读取用户配置文件的技术配置文件示例如下:

      <TechnicalProfile Id="REST-fetchUserProfile-APAC">
        <DisplayName>fetch user profile cross tenant</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://microsoftgraph.chinacloudapi.cn/beta/me</Item>
          <Item Key="AuthenticationType">Bearer</Item>
          <Item Key="UseClaimAsBearerToken">graph_bearerToken</Item>
          <Item Key="SendClaimsIn">Body</Item>
        </Metadata>
        <InputClaims>
          <InputClaim ClaimTypeReferenceId="graph_bearerToken" />
        </InputClaims>
        <OutputClaims>
          <OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="id" />
          <OutputClaim ClaimTypeReferenceId="givenName" />
          <OutputClaim ClaimTypeReferenceId="surName" />
          <OutputClaim ClaimTypeReferenceId="displayName" />
          <OutputClaim ClaimTypeReferenceId="userPrincipalName" PartnerClaimType="upn" />
          <OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="localAccountAuthentication" />
        </OutputClaims>
        <UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
      </TechnicalProfile>
      

密码重置自定义策略配置

在密码重置期间,我们必须确定用户配置文件位置,并针对用户配置文件所在的 Azure AD B2C 租户更新密码。

修改 Azure AD B2C 初学者包中的 LocalAccountSignUpWithLogonEmail 技术配置文件以执行用户的用户区域查找,并更新相应租户中的密码。 将 ValidationTechnicalProfiles 更新为:

<TechnicalProfile Id="LocalAccountDiscoveryUsingEmailAddress">
  <OutputClaims>
  ...
  <OutputClaim ClaimTypeReferenceId="ext_Api_bearerToken" DefaultValue="EMEA"/>
  </OutputClaims>
  <ValidationTechnicalProfiles>
    <ValidationTechnicalProfile ReferenceId="REST-getTokenforExternalApiCalls">
      <Preconditions>
        <Precondition Type="ClaimEquals" ExecuteActionsIf="true">
          <Value>user_region</Value>
          <Value>EMEA</Value>
          <Action>SkipThisValidationTechnicalProfile</Action>
        </Precondition>
      </Preconditions>
    </ValidationTechnicalProfile>
    <ValidationTechnicalProfile ReferenceId="REST-regionLookup" />
    <ValidationTechnicalProfile ReferenceId="AAD-UserReadUsingEmailAddress" />
  </ValidationTechnicalProfiles>
</TechnicalProfile>

当用户提交经过验证的电子邮件以更新其密码时,ValidationTechnicalProfiles 将执行以下逻辑:

  1. 获取令牌以调用受保护的 API 终结点

  2. 通过安全外部 REST API 终结点查找用户-区域映射

    • 此 API 调用在所有密码重置尝试之前进行,确保此 API 具有适当的负载均衡、复原能力和故障转移机制以符合运行时间要求至关重要。

修改 LocalAccountWritePasswordUsingObjectId 技术配置文件,以将新密码写入本地租户或按条件写入跨区域租户。

<TechnicalProfile Id="LocalAccountWritePasswordUsingObjectId">
  ...
  <ValidationTechnicalProfiles>
    <ValidationTechnicalProfile ReferenceId="AAD-UserWritePasswordUsingObjectId">
        <Preconditions>
          <Precondition Type="ClaimEquals" ExecuteActionsIf="false">
            <Value>user_region</Value>
            <Value>EMEA</Value>
            <Action>SkipThisValidationTechnicalProfile</Action>
          </Precondition>
        </Preconditions>
      </ValidationTechnicalProfile>
    <ValidationTechnicalProfile ReferenceId="REST-UserWritePasswordUsingObjectId-APAC">
        <Preconditions>
          <Precondition Type="ClaimEquals" ExecuteActionsIf="false">
            <Value>user_region</Value>
            <Value>APAC</Value>
            <Action>SkipThisValidationTechnicalProfile</Action>
          </Precondition>
        </Preconditions>
      </ValidationTechnicalProfile>
  </ValidationTechnicalProfiles>
</TechnicalProfile>

当用户提交新密码时,ValidationTechnicalProfiles 将执行以下逻辑:

  1. 如果用户存在于 EMEA 租户(此租户)中,则将用户新密码写入目录。

  2. 使用 REST API 调用按条件将新密码写入用户配置文件所在区域的用户配置文件。

    <TechnicalProfile Id="REST-UserWritePasswordUsingObjectId-APAC">
      <DisplayName>Write password to APAC tenant</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://myApi.com/writePasswordCrossTenant</Item>
        <Item Key="AuthenticationType">Bearer</Item>
        <Item Key="UseClaimAsBearerToken">ext_Api_bearerToken</Item>
        <Item Key="SendClaimsIn">Body</Item>
        <Item Key="DebugMode">true</Item>
      </Metadata>
      <InputClaims>
        <InputClaim ClaimTypeReferenceId="ext_Api_bearerToken" />
        <InputClaim ClaimTypeReferenceId="objectId" />
        <InputClaim ClaimTypeReferenceId="newPassword" />
      </InputClaims>
      <UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
    </TechnicalProfile>
    

后续步骤