重要
自 2025 年 5 月 1 日起,Azure AD B2C 将不再可供新客户购买。 在我们的常见问题解答中了解详细信息。
OpenID Connect 是构建在 OAuth 2.0 基础之上的身份验证协议,可用于将用户安全登录到 Web 应用程序。 通过使用 OpenID Connect 的 Azure Active Directory B2C (Azure AD B2C) 实现,可以将 Web 应用程序中的注册、登录和其他标识管理体验转移到 Microsoft Entra 中。 本指南演示如何使用与语言无关的方式执行此操作。 介绍在不使用我们的任何开放源代码库的情况下,如何发送和接收 HTTP 消息。
注意
大多数开源身份验证库都会获取并验证应用程序的 JWT。 我们建议浏览这些选项,而不是实现自己的代码。 有关详细信息,请参阅 Microsoft 身份验证库 (MSAL) 概述和 Microsoft 标识 Web 身份验证库。
OpenID Connect 扩展了 OAuth 2.0 授权协议,将其用作身份验证协议。 使用此身份验证协议可以执行单一登录。 它引入了 ID 令牌的概念,可让客户端验证用户的标识,并获取有关用户的基本配置文件信息。
OpenID Connect 还能使应用程序安全地获取访问令牌。 可以使用访问令牌访问授权服务器保护的资源。 如果您正在构建托管在服务器上并通过浏览器访问的 Web 应用程序,我们建议使用 OpenID Connect。 有关令牌的详细信息,请参阅 Azure Active Directory B2C 中的令牌概述
Azure AD B2C 扩展了标准 OpenID Connect 协议,使其功能远远超出了简单的身份验证和授权。 它引入了用户流参数,可让你使用 OpenID Connect 向应用程序添加注册、登录和配置文件管理等用户体验。
发送身份验证请求
当 Web 应用程序需要对用户进行身份验证并运行用户流时,它可以将用户定向到 /authorize 终结点。 用户可以根据用户流执行操作。
在此请求中,客户端指示需要在 scope 参数中从用户获取的权限,并指定要运行的用户流。 若要大致了解请求的工作方式,请将该请求粘贴到浏览器中并运行它。 替换:
-
{tenant}替换为你的租户名称。 -
00001111-aaaa-2222-bbbb-3333cccc4444使用你在租户中注册的应用程序的应用 ID。 - 将
{application-id-uri}/{scope-name}替换为你在租户中注册的应用程序的应用程序 ID URI 和作用域。 - 将
{policy}替换为你租户中的策略名称,例如b2c_1_sign_in。
GET /{tenant}.partner.onmschina.cn/{policy}/oauth2/v2.0/authorize?
Host: {tenant}.b2clogin.cn
client_id=00001111-aaaa-2222-bbbb-3333cccc4444
&response_type=code+id_token
&redirect_uri=https%3A%2F%2Fjwt.ms%2F
&response_mode=fragment
&scope=openid%20offline_access%20{application-id-uri}/{scope-name}
&state=arbitrary_data_you_can_receive_in_the_response
&nonce=12345
| 参数 | 必填 | 说明 |
|---|---|---|
| {租户} | 是 |
Azure AD B2C 租户的名称。 如果您使用的是 自定义域名,请将 tenant.b2clogin.cn 替换为您的域名,例如 fabrikam.com。 |
| {政策} | 是 | 应用运行的用户流或策略。 指定在 Azure AD B2C 租户中创建的用户流的名称。 例如:b2c_1_sign_in、b2c_1_sign_up 或 b2c_1_edit_profile。 |
| 客户编号 | 是 | Azure 门户分配给应用程序的应用程序 ID。 |
| nonce | 建议 | 请求中包含的一个值(由应用程序生成),并作为声明包含在生成的 ID 令牌中。 应用程序随后可以验证该值,以降低令牌重放攻击的风险。 此值通常是随机的唯一字符串,可用以识别请求的来源。 |
| response_type(响应类型) | 是 | 必须包含用于 OpenID Connect 的 ID 令牌。 如果 Web 应用程序还需要使用令牌来调用 Web API,则可以使用 code+id_token。 |
| 作用域 | 是 | 以空格分隔的作用域列表。
openid 作用域表示允许使用 ID 令牌的形式使用户登录并获取有关用户的数据。
offline_access 范围对 Web 应用程序是可选的。 它表明,你的应用程序需要一个刷新令牌来延长对资源的访问权限。
https://{tenant-name}/{app-id-uri}/{scope} 指示对受保护资源(例如 Web API)的权限。 有关详细信息,请参阅请求访问令牌。 |
| 提示 | 否 | 需要的用户交互类型。 此时唯一有效的值为 login,这会强制用户在该请求上输入其凭据。 |
| 重定向URI | 是 | 应用程序的 redirect_uri 参数,服务器通过该参数向应用程序发送身份验证响应。 它必须完全匹配在 Azure 门户中注册的其中一个 redirect_uri 参数,但必须经过 URL 编码。 |
| 响应模式 | 否 | 用于将授权码发送回您的应用程序的方法。 这可以是 query、form_post 或 fragment。 建议使用 form_post 响应模式获得最佳安全性。 |
| 状态 | 否 | 一个值,你可在请求中包含该值,授权服务器会在令牌响应中返回该值。 它可以是包含你想要的任何内容的字符串。 随机生成的唯一值通常用于防止跨站点请求伪造攻击。 该状态也用于在身份验证请求出现之前,在应用程序中编码用户的状态信息,例如用户之前所在的页面。 如果你不想在 Azure 门户中注册多个重定向 URL,可以使用 state 参数,以将应用程序中的响应与 Azure AD B2C 服务中的响应区分开(由于请求不同)。 |
| 登录提示 | 否 | 可用于预先填充登录页面的“登录名”字段。 有关详细信息,请参阅预填充登录名。 |
| domain_hint | 否 | 向 Azure AD B2C 提供有关应该用于登录的社交标识提供者的提示。 如果包含了有效的值,用户将直接转到标识提供者登录页面。 有关详细信息,请参阅将登录重定向到社交服务提供商。 |
| 自定义参数 | 否 | 可与自定义策略配合使用的自定义参数。 例如,动态自定义页面内容 URI 或键值声明解析程序。 |
此时,要求用户完成工作流。 用户可能需要输入其用户名和密码、用社交标识登录,或注册目录。 步骤数量还可能有所不同,具体取决于用户流程是如何定义的。
用户完成用户流后,会使用 redirect_uri 参数中指定的方法,将响应返回到 response_mode 参数中指定的应用程序。 对于上述每种情况,响应均相同,而与用户流无关。
使用 response_mode=fragment 的成功的响应如下所示:
GET https://jwt.ms/#
id_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5HVEZ2ZEstZnl0aEV1Q...
&code=AwABAAAAvPM1KaPlrEqdFSBzjqfTGBCmLdgfSTLEMPGYuNHSUYBrq...
&state=arbitrary_data_you_can_receive_in_the_response
| 参数 | 说明 |
|---|---|
| id_token(身份验证令牌) | 应用程序请求的 ID 令牌。 可以使用 ID 令牌验证用户的标识,开始与用户建立会话。 |
| 代码 | 如果使用了 response_type=code+id_token,则为应用程序请求的授权代码。 应用程序可以使用该授权代码请求目标资源的访问令牌。 授权代码通常在约 10 分钟后即会过期。 |
| 状态 | 如果请求中包含 state 参数,响应中就应该出现相同的值。 应用程序需验证请求和响应中的 state 值是否相同。 |
错误响应也可能会被发送到 redirect_uri 参数,以便应用程序对它们进行恰当的处理:
GET https://jwt.ms/#
error=access_denied
&error_description=AADB2C90091%3a+The+user+has+cancelled+entering+self-asserted+information.%0d%0aCorrelation+ID%3a+xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx%0d%0aTimestamp%3a+xxxx-xx-xx+xx%3a23%3a27Z%0d%0a
&state=arbitrary_data_you_can_receive_in_the_response
| 参数 | 说明 |
|---|---|
| 错误 | 一个代码,可用于对发生的错误类型进行分类。 |
| 错误描述 | 帮助识别身份验证错误根本原因的特定错误消息。 |
| 状态 | 如果请求中包含 state 参数,响应中就应该出现相同的值。 应用程序需验证请求和响应中的 state 值是否相同。 |
验证 ID 令牌
仅收到一个 ID 令牌并不表示可以对用户进行身份验证。 根据应用程序的要求验证 ID 令牌的签名和令牌中的声明。 Azure AD B2C 使用 JSON Web 令牌 (JWT) 和公钥加密对令牌进行签名并验证其是否有效。
注意
大多数开源身份验证库都会验证应用程序的 JWT。 我们建议浏览这些选项,而不是实现自己的验证逻辑。 有关详细信息,请参阅 Microsoft 身份验证库 (MSAL) 概述和 Microsoft 标识 Web 身份验证库。
Azure AD B2C 具有 OpenID Connect 元数据终结点,允许应用程序在运行时获取有关 Azure AD B2C 的信息。 此信息包括终结点、令牌内容和令牌签名密钥。 B2C 租户中的每个用户流都有一个 JSON 元数据文档。 例如,b2c_1_sign_in 中 fabrikamb2c.partner.onmschina.cn 用户流的元数据文档位于:
https://fabrikamb2c.b2clogin.cn/fabrikamb2c.partner.onmschina.cn/b2c_1_sign_in/v2.0/.well-known/openid-configuration
此配置文档的一个属性为 jwks_uri,对于相同用户流,该属性的值为:
https://fabrikamb2c.b2clogin.cn/fabrikamb2c.partner.onmschina.cn/b2c_1_sign_in/discovery/v2.0/keys
若要确定签署 ID 令牌时使用的是哪个用户流,您有两个选项。 首先,用户流名称包含在 ID 令牌的 acr 声明中,请参阅表示用户流的声明。 另一个方法是在发出请求时在 state 参数的值中对用户流进行编码,然后对其进行解码以确定使用的用户流。 任意一种方法均有效。
从 OpenID Connect 元数据终结点获取元数据文档后,可以使用 RSA-256 公钥来验证 ID 令牌的签名。 此终结点上可能列出多个密钥,每个密钥使用 kid 声明进行标识。 ID 令牌的标头还包含 kid 声明,该声明指示哪个密钥用于对 ID 令牌进行签名。
若要从 Azure AD B2C 验证令牌,需要使用指数 (e) 和模数 (n) 生成公钥。 为此,需要了解如何使用所选编程语言生成公钥。 可在以下网页中找到有关使用 RSA 协议生成公钥的官方文档: https://tools.ietf.org/html/rfc3447#section-3.1
验证了 ID 令牌的签名后,还有各种声明需要验证。 例如:
- 验证
nonce声明以防止令牌重放攻击。 其值应为在登录请求中指定的内容。 - 验证
aud声明以确保已为应用程序颁发 ID 令牌。 其值应为应用程序的 ID。 - 验证
iat和exp声明以确保 ID 令牌未过期。
此外,还需要执行更多的一些验证。 OpenID Connect 核心规范中详细介绍了验证。根据情况,可能还希望验证其他声明。 一些常见的验证包括:
- 确保用户/组织已注册该应用程序。
- 确保用户拥有正确的授权/权限。
- 确保通过某种方式执行了一定强度的身份验证,例如,通过 Microsoft Entra 多重身份验证。
验证 ID 令牌后,便可以开始与用户的会话。 在应用程序中,可以使用 ID 令牌中的声明来获取用户的相关信息。 此信息的用途包括显示、记录和授权。
获取令牌
如果仅需要 Web 应用程序运行用户流,则可以跳过下面几个部分。 这些部分只适用于需要对某个 Web API(它受 Azure AD B2C 本身的保护)发出经过身份验证的调用的 Web 应用程序。
通过将 response_type=code+id_token 请求发送到 POST 终结点,可以将获取的授权代码(通过 /token 获取)兑换为所需资源的令牌。 在 Azure AD B2C 中,可以像往常一样通过在请求中指定其他 API 的范围来为这些 API 请求访问令牌。
还可以为应用自己的后端 Web API 请求访问令牌。 在这种情况下,你将使用应用的客户端 ID 作为请求的作用域,这样会生成一个访问令牌,并将该客户端 ID 作为其“受众”:
POST https://{tenant}.b2clogin.cn/{tenant}.partner.onmschina.cn/{policy}/oauth2/v2.0/token HTTP/1.1
Host: {tenant}.b2clogin.cn
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&client_id=00001111-aaaa-2222-bbbb-3333cccc4444
&scope=00001111-aaaa-2222-bbbb-3333cccc4444 offline_access
&code=AwABAAAAvPM1KaPlrEqdFSBzjqfTGBCmLdgfSTLEMPGYuNHSUYBrq...
&redirect_uri=urn:ietf:wg:oauth:2.0:oob
| 参数 | 必填 | 说明 |
|---|---|---|
| {租户} | 是 | Azure AD B2C 租户的名称 |
| {政策} | 是 | 用于获取授权代码的用户流。 无法在此请求中使用不同的用户流。 将此参数添加到查询字符串中,而不是添加到 POST 正文中。 |
| 客户编号 | 是 | Azure 门户分配给应用程序的应用程序 ID。 |
| 客户端密钥 | 是,在 Web 应用中 | 在 Azure 门户中生成的应用程序机密。 客户端密码在此流中用于 Web 应用场景,在其中客户端可以安全地存储客户端密码。 对于本机应用(公共客户端)场景,客户端密码不能安全地存储,因此不能在此流上使用。 如果使用客户端密码,请定期更改。 |
| 代码 | 是 | 你在用户流程开始时获取的授权代码。 |
| 授权类型 (grant_type) | 是 | 授权类型,对于授权码流程,该值必须为 authorization_code。 |
| 重定向URI | 否 | 您在其中收到授权代码的应用程序的 redirect_uri 参数。 |
| 作用域 | 否 | 以空格分隔的作用域列表。
openid 作用域表示允许使用 id_token 参数的形式使用户登录并获取有关用户的数据。 它可用于获取访问应用程序自身后端 Web API 的令牌,该 API 由与客户端相同的应用程序 ID 表示。
offline_access 权限范围表示,您的应用程序需要刷新令牌才能持续访问资源。 |
成功的令牌响应如下所示:
{
"not_before": "1442340812",
"token_type": "Bearer",
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5HVEZ2ZEstZnl0aEV1Q...",
"scope": "00001111-aaaa-2222-bbbb-3333cccc4444 offline_access",
"expires_in": "3600",
"expires_on": "1644254945",
"refresh_token": "AAQfQmvuDy8WtUv-sd0TBwWVQs1rC-Lfxa_NDkLqpg50Cxp5Dxj0VPF1mx2Z...",
}
| 参数 | 说明 |
|---|---|
| 不早于 | 令牌生效的纪元时间。 |
| 令牌类型 | 令牌类型值。
Bearer 是唯一支持的类型。 |
| 访问令牌 (access_token) | 您请求的已签名 JWT。 |
| 作用域 | 令牌的有效范围。 |
| 有效期 | 访问令牌有效的时间长度(以秒为单位)。 |
| 到期日期 | 访问令牌失效的纪元时间。 |
| 刷新令牌 | OAuth 2.0 刷新令牌。 应用程序可以使用此令牌,在当前令牌过期之后获取其他令牌。 刷新令牌可用于延长保留资源访问权限的时间。 必须在授权和令牌请求中使用范围 offline_access,才能接收刷新令牌。 |
错误响应如下所示:
{
"error": "invalid_grant",
"error_description": "AADB2C90080: The provided grant has expired. Please re-authenticate and try again. Current time: xxxxxxxxxx, Grant issued time: xxxxxxxxxx, Grant expiration time: xxxxxxxxxx\r\nCorrelation ID: xxxxxxxx-xxxx-xxxX-xxxx-xxxxxxxxxxxx\r\nTimestamp: xxxx-xx-16 xx:10:52Z\r\n"
}
| 参数 | 说明 |
|---|---|
| 错误 | 一个代码,可用于对发生的错误类型进行分类。 |
| 错误描述 | 帮助识别身份验证错误根本原因的消息。 |
使用令牌
你成功获取访问令牌后,可通过在 Authorization 标头中加入令牌的方式,在后端 Web API 请求中使用该令牌:
GET /tasks
Host: mytaskwebapi.com
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5HVEZ2ZEstZnl0aEV1Q...
刷新令牌
访问令牌和 ID 令牌的生存期较短。 过期后,必须将其刷新才能继续访问资源。 在刷新访问令牌时,Azure AD B2C 会返回新令牌。 刷新后的访问令牌的 nbf(不早于)、iat(签发时间)和 exp(过期时间)声明值将会更新。 所有其他声明值都类似于上一个访问令牌中的声明值。
通过向 /token 端点再次提交 POST 请求来刷新令牌。 此时,提供 refresh_token 参数而不是 code 参数:
POST https://{tenant}.b2clogin.cn/{tenant}.partner.onmschina.cn/{policy}/oauth2/v2.0/token HTTP/1.1
Host: {tenant}.b2clogin.cn
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token
&client_id=00001111-aaaa-2222-bbbb-3333cccc4444
&scope=openid offline_access
&refresh_token=AwABAAAAvPM1KaPlrEqdFSBzjqfTGBCmLdgfSTLEMPGYuNHSUYBrq...
&redirect_uri=urn:ietf:wg:oauth:2.0:oob
| 参数 | 必填 | 说明 |
|---|---|---|
| {租户} | 是 | Azure AD B2C 租户的名称 |
| {政策} | 是 | 用于获取原始刷新令牌的用户流。 无法在此请求中使用不同的用户流。 将此参数添加到查询字符串中,而不是添加到 POST 正文中。 |
| 客户编号 | 是 | Azure 门户分配给应用程序的应用程序 ID。 |
| 客户端密钥 | 是,在 Web 应用中 | 在 Azure 门户中生成的应用程序机密。 客户端密码在此流中用于 Web 应用场景,在其中客户端可以安全地存储客户端密码。 对于本机应用(公共客户端)场景,客户端密码不能安全地存储,因此不能用于此调用。 如果使用客户端密码,请定期更改。 |
| 授权类型 (grant_type) | 是 | 授权类型,在授权代码流的这一部分中必须为 refresh_token。 |
| 刷新令牌 | 是 | 在该流程第二部分中获取的原始刷新令牌。 必须在授权和令牌请求中使用范围 offline_access,才能接收刷新令牌。 |
| 重定向URI | 否 | 您在其中收到授权代码的应用程序的 redirect_uri 参数。 |
| 作用域 | 否 | 以空格分隔的作用域列表。
openid 作用域表示允许使用 ID 令牌的形式使用户登录并获取有关用户的数据。 它可以用于向应用程序的后端 Web API 发送令牌,该令牌使用和客户端相同的应用程序 ID 表示。
offline_access 权限范围表示,您的应用程序需要刷新令牌才能持续访问资源。 |
成功的令牌响应如下所示:
{
"not_before": "1442340812",
"token_type": "Bearer",
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5HVEZ2ZEstZnl0aEV1Q...",
"scope": "00001111-aaaa-2222-bbbb-3333cccc4444 offline_access",
"expires_in": "3600",
"refresh_token": "AAQfQmvuDy8WtUv-sd0TBwWVQs1rC-Lfxa_NDkLqpg50Cxp5Dxj0VPF1mx2Z...",
"refresh_token_expires_in": "1209600"
}
| 参数 | 说明 |
|---|---|
| 不早于 | 令牌生效的纪元时间。 |
| 令牌类型 | 令牌类型值。
Bearer 是唯一支持的类型。 |
| 访问令牌 (access_token) | 请求的已签名 JWT。 |
| 作用域 | 令牌的有效范围。 |
| 有效期 | 访问令牌有效的时间长度(以秒为单位)。 |
| 刷新令牌 | OAuth 2.0 刷新令牌。 应用程序可以使用此令牌,在当前令牌过期之后获取其他令牌。 刷新令牌可用于延长保留资源访问权限的时间。 |
| 刷新令牌过期时间 | 刷新令牌有效的时间长度(以秒为单位)。 |
错误响应如下所示:
{
"error": "invalid_grant",
"error_description": "AADB2C90129: The provided grant has been revoked. Please reauthenticate and try again.\r\nCorrelation ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\r\nTimestamp: xxxx-xx-xx xx:xx:xxZ\r\n",
}
| 参数 | 说明 |
|---|---|
| 错误 | 一个代码,可用于对发生的错误类型进行分类。 |
| 错误描述 | 帮助识别身份验证错误根本原因的消息。 |
发送注销请求
如果想要从应用程序中注销用户,只是清除应用程序的 Cookie 或者结束与用户的会话是不够的。 需将用户重定向到 Azure AD B2C 进行注销。如果没有这么做,那么用户可能可以在应用程序中重新进行身份验证,且无需再次输入其凭据。 有关详细信息,请参阅 Azure AD B2C 会话行为。
若要将用户注销,请将用户重定向到前面所述的 OpenID Connect 元数据文档中列出的 end_session_endpoint:
GET https://{tenant}.b2clogin.cn/{tenant}.partner.onmschina.cn/{policy}/oauth2/v2.0/logout?post_logout_redirect_uri=https%3A%2F%2Fjwt.ms%2F
| 参数 | 必填 | 说明 |
|---|---|---|
| {租户} | 是 | Azure AD B2C 租户的名称。 如果您使用的是 自定义域名,请将 tenant.b2clogin.cn 替换为您的域名,例如 fabrikam.com。 |
| {政策} | 是 | 在授权请求中指定的用户流。 例如,如果用户已使用 b2c_1_sign_in 用户流登录,请在注销请求中指定 b2c_1_sign_in。 |
| id_token_hint | 否 | 将先前签发的 ID 令牌传递给注销端点,作为有关最终用户当前与客户端的已认证会话的提示。
id_token_hint 可确保 post_logout_redirect_uri 已注册为 Azure AD B2C 应用程序设置中的回复 URL。 有关详细信息,请参阅保护注销重定向。 |
| 客户编号 | 否* |
Azure 门户分配给应用程序的应用程序 ID。 * 当使用 Application 隔离 SSO 配置,且注销请求中的 Require ID Token 设置为 No 时,此项为必填。 |
| 登出后重定向URI | 否 | 用户在成功注销后应重定向到的 URL。如果未包含此参数,Azure AD B2C 会向用户显示一条常规消息。 除非提供 id_token_hint,否则不应在 Azure AD B2C 应用程序设置中将此 URL 注册为回复 URL。 |
| 状态 | 否 | 如果在授权请求中包含 state 参数,则授权服务器在响应中将同一值返回给 post_logout_redirect_uri。 应用程序需验证请求和响应中的 state 值是否相同。 |
收到注销请求后,Azure AD B2C 会使 Azure AD B2C 基于 Cookie 的会话失效,并尝试从联合标识提供者处注销。 有关详细信息,请参阅单一注销。
确保注销重定向的安全
注销后,用户将被重定向到您在 post_logout_redirect_uri 参数中指定的 URI,而与您为应用程序指定的回复 URL 列表无关。 但是,如果传递了有效的 id_token_hint 并启用了“注销请求中需要 ID 令牌”,则在执行重定向之前,Azure AD B2C 将验证 的值是否与应用程序的某个已配置重定向 URI 相匹配。 如果没有为应用程序配置匹配的回复 URL,则会显示一条错误消息,而用户不会重定向。
若要在注销请求中设置所需的 ID 令牌,请参阅在 Azure Active Directory B2C 中配置会话行为。
相关内容
- 详细了解 Azure AD B2C 会话。