创建属性集合启动和提交事件的自定义身份验证扩展(预览版)
适用于: 员工租户 外部租户(了解详细信息)
本文介绍如何为客户扩展 Microsoft Entra 外部 ID 中的用户注册体验。 在客户注册用户流中,事件侦听器可用于在属性集合之前和属性提交时扩展属性集合过程:
OnAttributeCollectionStart 事件发生在属性集合步骤的开始,在属性集合页面呈现之前。 可以添加例如预填充值和显示阻止错误等操作。
OnAttributeCollectionSubmit 事件在用户输入并提交属性后发生。 可以添加验证或修改用户条目等操作。
除了为属性集合启动和提交事件创建自定义身份验证扩展外,还需要创建一个 REST API,用于定义要对每个事件执行的工作流操作。 可以使用任何编程语言、框架和托管环境来创建和托管 REST API。 本文介绍 C# Azure 函数的快速入门方法。 Azure Functions 用于在无服务器环境中运行代码,无需先创建虚拟机 (VM) 或发布 Web 应用程序。
先决条件
- 若要使用 Azure 服务(包括 Azure Functions),需要一个 Azure 订阅。 如果你没有现有的 Azure 帐户,可以注册免费试用帐户,或者在创建帐户时使用 Visual Studio 订阅权益。
- 注册和登录用户流。
步骤 1:创建自定义身份验证扩展 REST API(Azure 函数应用)
提示
本文中的步骤可能因开始使用的门户而略有不同。
在此步骤中,你将使用 Azure Functions 创建一个 HTTP 触发器函数 API。 函数 API 是用户流的业务逻辑的来源。 创建触发器函数后,可为以下任一事件配置它:
使用管理员帐户登录 Azure 门户。
在 Azure 门户菜单上或在门户主页中,选择“创建资源”。
搜索并选择“函数应用”,然后选择“创建”。
在“基本信息”页面上,使用下表中指定的函数应用设置:
设置 建议值 描述 订阅 订阅 要在其中创建新函数应用的订阅。 资源组 myResourceGroup 选择现有的资源组或创建新资源组,以便在其中创建函数应用。 函数应用名称 全局唯一名称 用于标识新函数应用的名称。 有效字符为 a-z
(不区分大小写)、0-9
和-
。发布 代码 用于发布代码文件或 Docker 容器的选项。 对于本教程,请选择“代码”。 运行时堆栈 .NET 你的首选编程语言。 对于本教程,请选择“.NET”。 版本 6 (LTS) 进程内 .NET 运行时的版本。 进程内表示可以在门户中创建和修改函数,这是本指南建议的操作 区域 首选区域 选择与你靠近或者与函数可以访问的其他服务靠近的区域。 操作系统 Windows 系统会根据你选择的运行时堆栈预先选择操作系统。 计划类型 消耗(无服务器) 定义如何将资源分配给 Function App 的托管计划。 选择“查看 + 创建”以查看应用配置选择,然后选择“创建”。 部署需要几分钟时间。
部署后,选择“转到资源”查看新函数应用。
1.1 创建 HTTP 触发器函数
创建 Azure 函数应用后,为需要通过 HTTP 请求调用的操作创建 HTTP 触发器函数。 HTTP 触发器将由 Microsoft Entra 自定义身份验证扩展引用和调用。
- 在函数应用的“概述”页中,选择“函数”窗格,然后选择“在 Azure 门户中创建”下的“创建函数”。
- 在“创建函数”窗口中,将“开发环境”属性保留为“在门户中开发”。 在“模板”下,选择“HTTP 触发器”。
- 在“模板详细信息”下,为“新函数”属性输入 CustomAuthenticationExtensionsAPI。
- 对于“授权级别”,请选择“函数”。
- 选择“创建”。
1.2 为 OnAttributeCollectionStart 配置 HTTP 触发器
- 在菜单中,选择“代码 + 测试”。
- 选择以下与要实现的方案相对应的选项卡:“继续”、“阻止”或“SetPrefillValues”。 将代码替换为提供的代码片段。
- 替换代码后,在顶部菜单中选择“获取函数 URL”,然后复制 URL。 在步骤 2:创建和注册自定义身份验证扩展的“目标 URL”中使用此 URL。
借助此 HTTP 触发器,用户可以继续注册流(如果不需要进一步的操作)。
#r "Newtonsoft.Json"
using System.Net;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json;
using System.Text;
public static async Task<object> Run(HttpRequest req, ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic request = JsonConvert.DeserializeObject(requestBody);
var actions = new List<ContinueWithDefaultBehavior>{
new ContinueWithDefaultBehavior { type = "microsoft.graph.attributeCollectionStart.continueWithDefaultBehavior"}
};
var dataObject = new Data {
type = "microsoft.graph.onAttributeCollectionStartResponseData",
actions= actions
};
dynamic response = new ResponseObject {
data = dataObject
};
// Send the response
return response;
}
public class ResponseObject
{
public Data data { get; set; }
}
[JsonObject]
public class Data {
[JsonProperty("@odata.type")]
public string type { get; set; }
public List<ContinueWithDefaultBehavior> actions { get; set; }
}
[JsonObject]
public class ContinueWithDefaultBehavior {
[JsonProperty("@odata.type")]
public string type { get; set; }
}
1.3 为 OnAttributeCollectionSubmit 配置 HTTP 触发器
- 在菜单中,选择“代码 + 测试”。
- 选择以下与要实现的方案相对应的选项卡:“继续”、“阻止”、“修改值”或“验证错误”。 将代码替换为提供的代码片段。
- 替换代码后,在顶部菜单中选择“获取函数 URL”,然后复制 URL。 在步骤 2:创建和注册自定义身份验证扩展的“目标 URL”中使用此 URL。
借助此 HTTP 触发器,用户可以继续注册流(如果不需要进一步的操作)。
#r "Newtonsoft.Json"
using System.Net;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json;
using System.Text;
public static async Task<object> Run(HttpRequest req, ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic request = JsonConvert.DeserializeObject(requestBody);
var actions = new List<ContinueWithDefaultBehavior>{
new ContinueWithDefaultBehavior { type = "microsoft.graph.attributeCollectionSubmit.continueWithDefaultBehavior"}
};
var dataObject = new Data {
type = "microsoft.graph.onAttributeCollectionSubmitResponseData",
actions= actions
};
dynamic response = new ResponseObject {
data = dataObject
};
// Send the response
return response;
}
public class ResponseObject
{
public Data data { get; set; }
}
[JsonObject]
public class Data {
[JsonProperty("@odata.type")]
public string type { get; set; }
public List<ContinueWithDefaultBehavior> actions { get; set; }
}
[JsonObject]
public class ContinueWithDefaultBehavior {
[JsonProperty("@odata.type")]
public string type { get; set; }
}
步骤 2:创建和注册自定义身份验证扩展
在此步骤中,你将注册自定义身份验证扩展,供 Microsoft Entra ID 用来调用你的 Azure 函数。 该自定义身份验证扩展包含有关 REST API 终结点、它从 REST API 分析的属性集合启动和提交事件以及如何对 REST API 进行身份验证的信息。
至少以应用程序管理员和身份验证管理员的身份登录到 Microsoft Entra 管理中心。
浏览到“标识”>“外部标识”>“自定义身份验证扩展”。
选择“创建自定义扩展”。
在“基本信息”中,选择 AttributeCollectionStart 事件或 AttributeCollectionSubmit 事件,然后选择“下一步”。 确保这与上一步中的配置匹配。
在“终结点配置”中填写以下属性:
- 名称 - 自定义身份验证扩展的名称。 例如“On Attribute Collection Event”。
- 目标 URL - Azure 函数 URL 的
{Function_Url}
。 - 说明 - 自定义身份验证扩展的说明。
选择“下一步”。
在“API 身份验证”中,选择“创建新的应用注册”选项以创建代表函数应用的应用注册。
为应用命名,例如“Azure Functions 身份验证事件 API”。
选择“下一步”。
选择“创建”,这会创建该自定义身份验证扩展和关联的应用程序注册。
2.2 授予管理员同意
创建自定义身份验证扩展后,向已注册的应用授予应用程序同意,从而允许自定义身份验证扩展对 API 进行身份验证。
- 浏览到“标识”>“外部标识”>“自定义身份验证扩展(预览版)”。
- 从列表中选择自定义身份验证扩展。
- 在“概述”选项卡上,选择“授予权限”按钮,向已注册的应用授予管理员同意。 自定义身份验证扩展使用
client_credentials
通过Receive custom authentication extension HTTP requests
权限对 Azure 函数应用进行身份验证。 选择“接受”。
步骤 3:将自定义身份验证扩展添加到用户流
现在,可以将自定义身份验证扩展与一个或多个用户流相关联。
注意
如果需要创建用户流,请按照为客户创建注册和登录用户流中的步骤操作。
3.1 将自定义身份验证扩展添加到现有用户流
至少以应用程序管理员和身份验证管理员的身份登录到 Microsoft Entra 管理中心
如果有权访问多个租户,请使用顶部菜单中的“设置”图标 切换到外部租户。
浏览到“标识”>“外部标识”>“用户流”。
从列表中选择用户。
选择“自定义身份验证扩展”。
在“自定义身份验证扩展”页上,可以将自定义身份验证扩展与用户流中的两个不同步骤相关联:
- “从用户收集信息之前”与 OnAttributeCollectionStart 事件相关联。 选择编辑铅笔。 仅显示为 OnAttributeCollectionStart 事件配置的自定义扩展。 选择为属性集合启动事件配置的应用程序,然后选择“选择”。
- “用户提交其信息时”与 OnAttributeCollectionSubmit 事件相关联。 仅显示为 OnAttributeCollectionSubmit 事件配置的自定义扩展。 选择为属性集合提交事件配置的应用程序,然后选择“选择”。
确保两个属性集合步骤旁边列出的应用程序都是正确的。
选择“保存”图标。
步骤 4:测试应用程序
若要获取令牌并测试自定义身份验证扩展,可以使用 https://jwt.ms 应用。 它是 Microsoft 拥有的一个 Web 应用程序,可显示令牌的解码内容(令牌内容永远不会离开浏览器)。
按照以下步骤注册 jwt.ms Web 应用程序:
4.1 注册 jwt.ms Web 应用程序
- 至少以应用程序管理员的身份登录到 Microsoft Entra 管理中心。
- 浏览到“标识”>“应用程序”>“应用程序注册”。
- 选择“新注册”。
- 输入应用程序的“名称”。 例如“我的测试应用程序”。
- 在“支持的帐户类型”下,选择“仅此组织目录中的帐户”。
- 在“重定向 URI”中的“选择平台”下拉列表中选择“Web”,然后在“URL”文本框中输入
https://jwt.ms
。 - 选择“注册”以完成应用注册。
4.2 获取应用程序 ID
在应用注册中的“概述”下,复制“应用程序(客户端) ID”。 在后续步骤中,该应用 ID 将引用为 <client_id>
。 在 Microsoft Graph 中,appId 属性引用了它。
4.3 启用隐式流
jwt.ms 测试应用程序使用隐式流。 按照以下步骤在“我的测试应用程序”注册中启用隐式流。
重要
Microsoft 建议使用最安全的可用身份验证流。 此过程中用于测试的身份验证流需要非常高的信任度,并且具有其他流中不存在的风险。 此方法不应用于向生产应用对用户进行身份验证(了解详细信息)。
- 在“管理”下,选择“身份验证”。
- 在“隐式授权和混合流”下,选中“ID 令牌(用于隐式流和混合流)”复选框。
- 选择“保存”。
步骤 5:保护 Azure 函数
Microsoft Entra 自定义身份验证扩展使用服务器到服务器的流来获取在 HTTP Authorization
标头中发送到 Azure 函数的访问令牌。 将函数发布到 Azure 时(尤其是在生产环境中),需要验证在授权标头中发送的令牌。
若要保护 Azure 函数,请按照以下步骤集成 Microsoft Entra 身份验证,以便使用 Azure Functions 身份验证事件 API 应用程序注册来验证传入的令牌。
注意
如果 Azure 函数应用所在的 Azure 租户不同于注册自定义身份验证扩展的租户,请跳到 5.1 使用 OpenID Connect 标识提供者步骤。
5.1 将标识提供者添加到 Azure 函数
登录 Azure 门户。
导航到之前发布的函数应用并选择它。
在左侧菜单中选择“身份验证”。
选择“添加标识提供者”。
选择“Microsoft”作为标识提供者。
选择“客户”作为租户类型。
在“应用注册”下,输入在注册自定义声明提供程序时先前创建的“Azure Functions 身份验证事件 API”应用注册的
client_id
。对于“颁发者 URL”,输入以下 URL
https://{domainName}.ciamlogin.com/{tenant_id}/v2.0
,其中{domainName}
是外部租户的域名。{tenantId}
是外部租户的租户 ID。 应在此处注册自定义身份验证扩展。
在“未经身份验证的请求”下,选择“HTTP 401 未授权”作为标识提供者。
取消选择“令牌存储”选项。
选择“添加”以将身份验证添加到 Azure 函数。
5.2 使用 OpenID Connect 标识提供者
如果已配置步骤 5:保护 Azure 函数,请跳过此步骤。 否则,如果 Azure 函数所在的租户不同于注册自定义身份验证扩展的租户,请执行以下步骤保护函数:
登录到 Azure 门户,导航到之前发布的函数应用并选择它。
在左侧菜单中选择“身份验证”。
选择“添加标识提供者”。
选择“OpenID Connect”作为标识提供者。
提供名称,例如Contoso Microsoft Entra ID。
在“元数据条目”下的“文档 URL”中输入以下 URL。 将
{tenantId}
替换为 Microsoft Entra 租户 ID。https://login.microsoftonline.com/{tenantId}/v2.0/.well-known/openid-configuration
在“应用注册”下,输入先前创建的“Azure Functions 身份验证事件 API”应用注册的应用程序 ID(客户端 ID)。
在 Microsoft Entra 管理中心中:
- 选择先前创建的“Azure Functions 身份验证事件 API”应用注册。
- 选择“证书和机密”>“客户端密码”>“新建客户端密码”。
- 添加客户端机密的说明。
- 选择机密的过期时间,或指定自定义的生存期。
- 选择“添加”。
- 记下机密值,以便在客户端应用程序代码中使用。 退出此页面后,此机密值永不再显示。
返回 Azure 函数,在“应用注册”下输入“客户端机密”。
取消选择“令牌存储”选项。
选择“添加”以添加 OpenID Connect 标识提供者。
步骤 6:测试应用程序
若要测试自定义身份验证扩展,请执行以下步骤:
打开新的隐私浏览器并导航到以下 URL:
https://<domainName>.ciamlogin.com/<tenant_id>/oauth2/v2.0/authorize?client_id=<client_id>&response_type=code+id_token&redirect_uri=https://jwt.ms&scope=openid&state=12345&nonce=12345
- 将
<domainName>
替换为外部租户名称,将<tenant-id>
替换为外部租户 ID。 - 将
<client_id>
替换为添加到用户流的应用程序的 ID。
- 将
登录后,你将在
https://jwt.ms
上看到解码的令牌。