使用 Azure Active Directory B2C 自定义策略调用 REST API

通过 Azure Active Directory B2C (Azure AD B2C) 自定义策略,可以和在 Azure AD B2C 外部实现的应用程序逻辑进行交互。 为此,需要对终结点进行 HTTP 调用。 Azure AD B2C 自定义策略可为此目的提供 RESTful 技术配置文件。 通过使用此功能,可以实现 Azure AD B2C 自定义策略中不可用的功能。

在本文中,学习如何:

  • 创建并部署要用作 RESTful 服务的示例 Node.js 应用。

  • 使用 RESTful 技术配置文件对 Node.js RESTful 服务进行 HTTP 调用。

  • 处理或报告 RESTful 服务向自定义策略返回的错误。

方案概述

使用 Azure AD B2C 自定义策略在用户旅程中创建分支中,选择“个人帐户”的用户需要提供有效的邀请访问代码才能继续操作。 我们使用静态访问代码,但实际应用无法以这种方式工作。 如果发出访问代码的服务在你的自定义策略之外,则必须调用该服务,并传递用户输入的访问代码以进行验证。 如果访问代码有效,则该服务将返回 HTTP 200 OK 响应,并且 Azure AD B2C 会颁发 JWT 令牌。 否则,服务将返回 HTTP 4xx 响应,用户必须重新输入访问代码。

调用 R E S T A P I 的流程图。

先决条件

注意

本文是《在 Azure Active Directory B2C 中创建和运行自己的自定义策略操作指南系列教程》的一部分。 建议从第一篇文章开始本系列教程。

步骤 1 - 创建并部署 Node.js 应用

你需要部署一个将会充当外部应用的应用。 然后,自定义策略将对此应用进行 HTTP 调用。

步骤 1.1 - 创建 Node.js 应用

  1. 创建一个用于托管 node 应用程序的文件夹,例如 access-code-app

  2. 在终端中,将目录更改为 Node 应用文件夹,例如 cd access-code-app,然后运行 npm init -y。 此命令将为 Node.js 项目创建一个默认 package.json 文件。

  3. 在终端中,运行 npm install express body-parser。 此命令将安装 Express 框架和 body-parser 包。

  4. 在你的项目中,创建 index.js 文件。

  5. 在 VS Code 中,打开 index.js 文件,并添加以下代码:

        const express = require('express');
        let bodyParser = require('body-parser')
        //Create an express instance
        const app = express();
    
        app.use( bodyParser.json() );       // to support JSON-encoded bodies
        app.use(bodyParser.urlencoded({     // to support URL-encoded bodies
          extended: true
        }));
    
    
        app.post('/validate-accesscode', (req, res) => {
            let accessCode = '88888';
            if(accessCode == req.body.accessCode){
                res.status(200).send();
            }else{
                let errorResponse = {
                    "version" :"1.0",
                    "status" : 409,
                    "code" : "errorCode",
                    "requestId": "requestId",
                    "userMessage" : "The access code you entered is incorrect. Please try again.",
                    "developerMessage" : `The provided code ${req.body.accessCode} does not match the expected code for user.`,
                    "moreInfo" :"https://learn.microsoft.com/en-us/azure/active-directory-b2c/string-transformations"
                };
                res.status(409).send(errorResponse);                
            }
        });
    
        app.listen(80, () => {
            console.log(`Access code service listening on port !` + 80);
        });
    

    你可以看到,当用户提交错误的访问代码时,可以直接从 REST API 返回错误。 自定义策略允许你返回 HTTP 4xx 错误消息,例如 400(错误请求)或 409(冲突)响应状态代码,响应 JSON 正文格式如 errorResponse 变量中所示。 可以从数据库读取应用中 accessCode 的源。 了解有关返回验证错误消息的详细信息。

  6. 若要测试应用是否按预期工作,请使用以下步骤:

    1. 在终端中,运行 node index.js 命令以启动应用服务器。
    2. 要发出类似于此示例所示的 POST 请求,可以使用 HTTP 客户端(如 Microsoft PowerShell)。
        POST http://localhost/validate-accesscode HTTP/1.1
        Host: localhost
        Content-Type: application/x-www-form-urlencoded
    
        accessCode=user-code-code
    

    user-code-code 替换为用户输入的访问代码,例如 54321。 如果你使用的是 PowerShell,请运行以下脚本。

        $accessCode="54321"
        $endpoint="http://localhost/validate-accesscode"
        $body=$accessCode
        $response=Invoke-RestMethod -Method Post -Uri $endpoint -Body $body
        echo $response
    

    如果你使用了错误的访问代码,则响应将类似于以下 JSON 代码片段:

        {
            "version": "1.0",
            "status": 409,
            "code": "errorCode",
            "requestId": "requestId",
            "userMessage": "The access code you entered is incorrect. Please try again.",
            "developerMessage": "The provided code 54321 does not match the expected code for user.",
            "moreInfo": "https://learn.microsoft.com/en-us/azure/active-directory-b2c/string-transformations"
        }
    

你的 REST 服务可以返回 HTTP 4xx 状态代码,但 JSON 响应中 status 的值必须是 409

此时,你已准备好部署 Node.js 应用。

步骤 1.2 - 在 Azure 应用服务 中部署 Node.js 应用

要支持自定义策略访问 Node.js 应用,它必须是可访问的,因此需要部署它。 在本文中,你将通过使用 Azure 应用服务来部署应用,但需要使用替代托管方法。

按照将应用部署到 Azure 中的步骤,将 Node.js 应用部署到 Azure。 对于应用的名称,请使用描述性名称,例如 custompolicyapi。 因此:

  • 应用 URL 类似于 https://custompolicyapi.chinacloudsites.cn

  • 服务终结点类似于 https://custompolicyapi.chinacloudsites.cn/validate-accesscode

可以使用 HTTP 客户端(如 Microsoft PowerShell)测试已部署的应用。 这次使用 https://custompolicyapi.chinacloudsites.cn/validate-accesscode URL 作为终结点。

步骤 2 - 调用 REST API

现在,应用正在运行,你需要通过自定义策略进行 HTTP 调用。 Azure AD B2C 自定义策略提供用于调用外部服务的 RESTful 技术配置文件

步骤 2.1 - 定义 RESTful 技术配置文件

ContosoCustomPolicy.XML 文件中,找到 ClaimsProviders 部分,然后使用以下代码定义新的 RESTful 技术配置文件:

    <!--<ClaimsProviders>-->
        <ClaimsProvider>
            <DisplayName>HTTP Request Technical Profiles</DisplayName>
            <TechnicalProfiles>
                <TechnicalProfile Id="ValidateAccessCodeViaHttp">
                    <DisplayName>Check that the user has entered a valid access code by using Claims Transformations</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://custompolicyapi.chinacloudsites.cn/validate-accesscode</Item>
                        <Item Key="SendClaimsIn">Body</Item>
                        <Item Key="AuthenticationType">None</Item>
                        <Item Key="AllowInsecureAuthInProduction">true</Item>
                    </Metadata>
                    <InputClaims>
                        <InputClaim ClaimTypeReferenceId="accessCode" PartnerClaimType="accessCode" />
                    </InputClaims>
                </TechnicalProfile>
            </TechnicalProfiles>
        </ClaimsProvider>
    <!--</ClaimsProviders>-->

从协议中,可以看到我们将技术配置文件配置为使用 RestfulProvider。 你还可以在元数据部分看到以下信息:

  • ServiceUrl 表示 API 终结点。 其值为 https://custompolicyapi.chinacloudsites.cn/validate-accesscode。 如果使用替代方法部署了 Node.js 应用,请确保更新终结点值。

  • SendClaimsIn 指定如何将输入声明发送到 RESTful 声明提供程序。 可能的值:Body (default)FormHeaderUrlQueryString 使用 Body 时,如本文所示,调用 POST HTTP 谓词,以及发送到 API 的数据(如果在请求正文中格式化为键值对)。 了解如何调用 GET HTTP 谓词,以及如何将数据作为查询字符串传递

  • AuthenticationType 指定 RESTful 声明提供程序执行的身份验证类型。 我们的 RESTful 声明提供程序将调用不受保护的终结点,因此我们将 AuthenticationType 设置为“”。 如果将身份验证类型设置为 Bearer,则需要添加 CryptographicKeys 元素,它用于指定访问令牌的存储。 详细了解 RESTful 声明提供程序支持的身份验证类型

  • InputClaim 中的 PartnerClaimType 属性可指定在 API 中接收数据的方式。

步骤 2.2 - 更新验证技术配置文件

使用 Azure AD B2C 自定义策略在用户旅程中创建分支中,你使用声明转换验证了 accessCode。 在本文中,你将通过对外部服务进行 HTTP 调用来验证 accessCode。 因此,需要更新自定义策略来反映新方法。

找到 AccessCodeInputCollector 技术配置文件,然后将 ValidationTechnicalProfile 元素的 ReferenceId 更新为 ValidateAccessCodeViaHttp

from:

    <ValidationTechnicalProfile ReferenceId="CheckAccessCodeViaClaimsTransformationChecker"/>

to:

    <ValidationTechnicalProfile ReferenceId="ValidateAccessCodeViaHttp"/>

此时,不需要包含 IdCheckAccessCodeViaClaimsTransformationChecker 的技术配置文件,可以将其删除。

步骤 3 - 上传自定义策略文件

确保 Node.js 应用正在运行,然后按照上传自定义策略文件中的步骤上传策略文件。 如果要上传与门户中已有文件同名的文件,请确保选择“覆盖自定义策略(若已有)”。

步骤 4 - 测试自定义策略

按照测试自定义策略中的步骤测试自定义策略:

  1. 对于“帐户类型”,请选择“个人帐户
  2. 根据需要输入其余详细信息,然后选择“继续”。 你会看到一个新屏幕。
  3. 对于“访问代码”,输入 88888,然后选择“继续”。 策略执行完成后,系统会将你重定向到 https://jwt.ms,然后你会看到已解码的 JWT 令牌。 如果重复该过程,并输入除 88888 以外的其他访问代码,则会看到错误输入的访问代码不正确。请重试。

步骤 5 - 启用调试模式

在开发过程中,你可能想要查看 API 发送的详细错误,例如 developerMessagemoreInfo。 在这种情况下,需要在 RESTful 技术提供程序中启用调试模式。

  1. 找到 ValidateAccessCodeViaHttp 技术提供程序,然后在该技术提供程序的 metadata 中添加以下项:

        <Item Key="DebugMode">true</Item>
    
  2. 保存更改并上传策略文件

  3. 测试自定义策略。 请确保为访问代码使用错误的输入。 你会看到类似于以下屏幕截图中显示的错误:

    启用调试模式时出现的错误屏幕截图。

处理复杂的请求 JSON 有效负载

如果调用的 REST API 要求发送复杂的 JSON 有效负载,则可以使用 GenerateJson JSON 声明转换来创建有效负载。 生成有效负载后,可以使用 ClaimUsedForRequestPayload 元数据选项来指定包含 JSON 有效负载的声明的名称。

例如,使用以下声明转换生成 JSON 有效负载:

    <ClaimsTransformation Id="GenerateRequestBodyClaimsTransformation" TransformationMethod="GenerateJson">
        <InputClaims>
            <InputClaim ClaimTypeReferenceId="email" TransformationClaimType="customerEntity.email" />
            <InputClaim ClaimTypeReferenceId="objectId" TransformationClaimType="customerEntity.userObjectId" />
            <InputClaim ClaimTypeReferenceId="givenName" TransformationClaimType="customerEntity.firstName" />
            <InputClaim ClaimTypeReferenceId="surname" TransformationClaimType="customerEntity.lastName" />
            <InputClaim ClaimTypeReferenceId="accessCode" TransformationClaimType="customerEntity.accessCode" />
        </InputClaims>
        <InputParameters>
            <InputParameter Id="customerEntity.role.name" DataType="string" Value="Administrator" />
            <InputParameter Id="customerEntity.role.id" DataType="long" Value="1" />
        </InputParameters>
        <OutputClaims>
            <OutputClaim ClaimTypeReferenceId="requestBodyPayload" TransformationClaimType="outputClaim" />
        </OutputClaims>
    </ClaimsTransformation>

ClaimsTransformation 将生成以下 JSON 对象:

{
   "customerEntity":{
      "email":"john.s@contoso.com",
      "userObjectId":"01234567-89ab-cdef-0123-456789abcdef",
      "firstName":"John",
      "lastName":"Smith",
      "accessCode":"88888",
      "role":{
         "name":"Administrator",
         "id": 1
      }
   }
}

然后,更新 RESTful 技术提供程序的 MetadataInputClaimsTransformationsInputClaims,如下所示:

    <Metadata>
        <Item Key="ClaimUsedForRequestPayload">requestBodyPayload</Item>
        <!--Other Metadata items -->
    </Metadata>
    
    <!--Execute your InputClaimsTransformations to generate your request Payload-->
    <InputClaimsTransformations>
        <InputClaimsTransformation ReferenceId="GenerateRequestBodyClaimsTransformation" />
    </InputClaimsTransformations>
    
    <InputClaims>
        <InputClaim ClaimTypeReferenceId="requestBodyPayload" />
    </InputClaims>

从 REST API 接收数据

如果 REST API 返回要作为声明包含在策略中的数据,可以通过在 RESTful 技术配置文件的元素OutputClaims中指定声明来接收数据。 如果策略中定义的声明的名称与 REST API 中定义的名称不同,则需要使用PartnerClaimType属性映射这些名称。

后续步骤

接下来,了解: