Microsoft 标识平台上的 OpenID Connect

OpenID Connect (OIDC) 扩展了 OAuth 2.0 授权协议,使其也可用作身份验证协议。 可以使用 OIDC 通过一个称作“ID 令牌”的安全令牌在支持 OAuth 的应用程序之间启用单一登录 (SSO)。

可以 OpenID Foundation 网站上的 OpenID Connect Core 1.0 specification(OpenID Connect Core 1.0 规范)中找到 OIDC 的完整规范。

协议流:登录

下图显示了基本的 OpenID Connect 登录流程。 本文的后续部分将更详细地介绍该流中的步骤。

Swim-lane diagram showing the OpenID Connect protocol's sign-in flow.

提示

Try running this request in Postman
尝试在 Postman 中执行此请求和其他操作 - 不要忘记替换令牌和 ID!

启用 ID 令牌

当客户端应用程序在用户身份验证期间请求一个 ID 令牌时,授权服务器(Microsoft 标识平台)会颁发 OpenID Connect 引入的 ID 令牌。 客户端应用程序可以使用该 ID 令牌验证用户的身份并获取有关用户的其他信息(声明)。

默认情况下,不会为注册到 Microsoft 标识平台的应用程序颁发 ID 令牌。 应用程序的 ID 令牌将通过以下方法之一启用:

  1. 登录到 Microsoft Entra 管理中心
  2. 浏览到“标识”>“应用程序”>“应用注册”><你的应用程序>>“身份验证”。
  3. 在“平台配置”下,选择“添加平台” 。
  4. 在打开的窗格中,为应用程序选择相应的平台。 例如,对于 Web 应用程序,请选择“Web”。
  5. 在“重定向 URI”下,添加应用程序的重定向 URI。 例如 https://localhost:8080/
  6. 在“隐式授权和混合流”下,选中“ID 令牌(用于隐式流和混合流)”复选框。

或:

  1. 选择“标识”>“应用程序”>“应用注册”><你的应用程序>>“清单”。
  2. 在应用注册的应用程序清单中将 oauth2AllowIdTokenImplicitFlow 设置为 true

如果你的应用未启用 ID 令牌,但却请求提供一个 ID 令牌,Microsoft 标识平台将返回如下所示的 unsupported_response 错误:

为输入参数 'response_type' 提供的值不允许用于此客户端。 预期值为“code”

本文稍后的发送登录请求中介绍了如何通过指定 id_tokenresponse_type 来请求 ID 令牌。

提取 OpenID 配置文档

OpenID 提供者(例如 Microsoft 标识平台)在可公开访问的终结点上提供 OpenID 提供者配置文档,其中包含提供者的 OIDC 终结点、支持的声明和其他元数据。 客户端应用程序可以使用元数据来发现用于身份验证的 URL 和身份验证服务的公共签名密钥。

身份验证库是 OpenID 配置文档的最常见使用者,它们用于发现身份验证 URL、提供者的公共签名密钥和其他服务元数据。 如果在应用中使用了身份验证库,那么你可能不需要手动对发送至 OpenID 配置文档终结点的请求和其返回的响应进行编码。

查找应用的 OpenID 配置文档 URI

为 Microsoft Entra ID 中的每个应用注册提供了一个可公开访问的终结点,该终结点为其 OpenID 配置文档提供服务。 若要确定应用的配置文档终结点的 URI,请将已知的 OpenID 配置路径追加到应用注册的颁发机构 URL。

  • 已知的配置文档路径:/.well-known/openid-configuration
  • 颁发机构 URL:https://login.partner.microsoftonline.cn/{tenant}/v2.0

{tenant} 的值因应用程序的登录受众而异,如下表中所示。 颁发机构 URL 也因云实例而异。

说明
common 在 Microsoft Entra ID 中具有工作或学校帐户的用户可以登录到应用程序。
organizations 只有在 Microsoft Entra ID 中具有工作或学校帐户的用户可以登录到应用程序。
8eaef023-2b34-4da1-9baa-8bc8c9d6a490contoso.partner.onmschina.cn 只有来自特定 Microsoft Entra 租户的用户(具有工作或学校帐户的目录成员)才能登录到应用程序。

该值可以是 Microsoft Entra 租户的域名或 GUID 格式的租户 ID。

若要在 Microsoft Entra 管理中心查找 OIDC 配置文档,请登录到 Microsoft Entra 管理中心,然后执行以下操作:

  1. 浏览到“标识”>“应用程序”>“应用注册”><你的应用程序>>“终结点”。
  2. 在“OpenID Connect 元数据文档”下找到 URI。

示例请求

以下请求会从由世纪互联运营的 Microsoft Azure 上的 common 颁发机构 OpenID 配置文档终结点中获取 OpenID 配置元数据:

GET /common/v2.0/.well-known/openid-configuration
Host: login.partner.microsoftonline.cn

提示

试试看! 若要查看应用程序 common 颁发机构的 OpenID 配置文档,请导航到 https://login.partner.microsoftonline.cn/common/v2.0/.well-known/openid-configuration

示例响应

配置元数据以 JSON 格式返回,如以下示例所示(为简洁起见,内容已被截断)。 OpenID Connect 1.0 发现规范中详细介绍了 JSON 响应中返回的元数据。

{
  "authorization_endpoint": "https://login.partner.microsoftonline.cn/{tenant}/oauth2/v2.0/authorize",
  "token_endpoint": "https://login.partner.microsoftonline.cn/{tenant}/oauth2/v2.0/token",
  "token_endpoint_auth_methods_supported": [
    "client_secret_post",
    "private_key_jwt"
  ],
  "jwks_uri": "https://login.partner.microsoftonline.cn/{tenant}/discovery/v2.0/keys",
  "userinfo_endpoint": "https://microsoftgraph.chinacloudapi.cn/oidc/userinfo",
  "subject_types_supported": [
      "pairwise"
  ],
  ...
}

发送登录请求

若要对用户进行身份验证并请求要在应用程序中使用的 ID 令牌,请将其用户代理定向到 Microsoft 标识平台的 /authorize 终结点。 该请求类似于 OAuth 2.0 授权代码流的第一个阶段,但有以下差异:

  • scope 参数中包含 openid 范围。
  • response_type 参数中指定 id_token
  • 包含 nonce 参数。

示例登录请求(包含换行符只是为了便于阅读):

GET https://login.partner.microsoftonline.cn/{tenant}/oauth2/v2.0/authorize?
client_id=535fb089-9ff3-47b6-9bfb-4f1264799865
&response_type=id_token
&redirect_uri=http%3A%2F%2Flocalhost%2Fmyapp%2F
&response_mode=form_post
&scope=openid
&state=12345
&nonce=678910
参数 条件 描述
tenant 必需 可以在请求路径中使用 {tenant} 值来控制谁可以登录到应用程序。 可以使用的值包括 commonorganizationsconsumers 和租户标识符。 有关详细信息,请参见协议基础知识。 至关重要的是,对于用户从一个租户登录到另一个租户的来宾场景,必须提供租户标识符才能让其正确登录到资源租户。
client_id 必须 Microsoft Entra 管理中心 - 应用注册体验分配给应用的应用程序(客户端)ID
response_type 必须 必须包含 OpenID Connect 登录的 id_token
redirect_uri 建议 应用的重定向 URI,应用可在其中发送和接收身份验证响应。 它必须与门户中注册的其中一个重定向 URI 完全匹配,否则必须经过 URL 编码。 如果不存在,终结点会随机选取一个已注册的 redirect_uri,以将用户重定向回此 URI。
scope 必需 范围的空格分隔列表。 针对 OpenID Connect,即必须包含范围 openid,其在同意 UI 中转换为“你将登录”权限。 也可以在此请求中包含其他范围来请求许可。
nonce 必需 应用在其 ID 令牌请求中生成并发送的值。 Microsoft 标识平台返回给应用的 ID 令牌中包含相同的 nonce 值。 为了缓解令牌重放攻击,应用应验证 ID 令牌中的 nonce 值是否与应用在请求该令牌时发送的值相同。 该值通常是唯一的随机字符串。
response_mode 建议 指定将生成的授权代码发回给应用时应该使用的方法。 可以是 form_postfragment。 对于 Web 应用程序,建议使用 response_mode=form_post,确保以最安全的方式将令牌传输到应用程序。
state 建议 同样随令牌响应返回的请求中所包含的值。 可以是所需的任何内容的字符串。 随机生成的唯一值通常用于 防范跨站点请求伪造攻击。 该状态还用于在身份验证请求出现之前,在应用中编码用户的状态信息,例如用户过去所在的页面或视图。
prompt 可选 表示需要的用户交互类型。 此时唯一有效的值为 loginnoneconsentselect_accountprompt=login 声明强制用户在该请求上输入凭据,从而使单一登录无效。 prompt=none 参数与之相反,应与 login_hint 配合使用,以指示必须登录的用户。 这些参数确保用户根本不会看到交互式提示。 如果请求无法通过单一登录以无提示方式完成,则 Microsoft 标识平台将返回一个错误。 原因包括没有任何用户登录、提示的用户未登录,或多个用户登录但未向其提供提示。 prompt=consent 声明将在用户登录后触发 OAuth 同意对话框。 该对话框要求用户向应用授予权限。 最后,select_account 向用户显示帐户选择器,取消无提示 SSO,但允许用户选取他们登录时想要使用的帐户,无需输入凭据。 不能同时使用 login_hintselect_account
login_hint 可选 如果事先知道用户名,可以使用此参数预先填充用户登录页的用户名/电子邮件地址字段。 通常,应用在已经从前次登录提取 login_hint可选声明后,会在重新身份验证时使用此参数。
domain_hint 可选 联合目录中的用户领域。 这会跳过用户在登录页面上经历的基于电子邮件的发现过程,从而实现更加流畅的用户体验。 对于通过本地目录(例如 AD FS)联合的租户,这通常会由于现有登录会话而导致无缝登录。

此时,系统会提示用户输入凭据并完成身份验证。 Microsoft 标识平台将验证用户是否已许可 scope 查询参数中指定的权限。 如果用户未许可其中的任何权限,Microsoft 标识平台会提示用户许可所需的权限。 阅读有关权限、许可与多租户应用的详细信息。

用户经过身份验证并许可后,Microsoft 标识平台会使用 response_mode 参数中指定的方法,将响应返回到位于指定重定向 URI 处的应用。

成功的响应

使用 response_mode=form_post 时,成功的响应类似于:

POST /myapp/ HTTP/1.1
Host: localhost
Content-Type: application/x-www-form-urlencoded

id_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik1uQ19WWmNB...&state=12345
参数 说明
id_token 应用请求的 ID 令牌。 可以使用 id_token 参数验证用户的标识,开始与用户建立会话。 有关 ID 令牌及其内容的详细信息,请参阅 ID 令牌参考
state 如果请求中包含 state 参数,响应中就应该出现相同的值。 应用应该验证请求和响应中的 state 值是否完全相同。

错误响应

也可以将错误响应发送到重定向 URI,使应用能够处理这些响应,例如:

POST /myapp/ HTTP/1.1
Host: localhost
Content-Type: application/x-www-form-urlencoded

error=access_denied&error_description=the+user+canceled+the+authentication
参数 说明
error 可用于对发生的错误分类以及对错误做出反应的错误代码字符串。
error_description 帮助识别身份验证错误根本原因的特定错误消息。

授权终结点错误的错误代码

下表描述了可在错误响应的 error 参数中返回的错误代码:

错误代码 说明 客户端操作
invalid_request 协议错误,例如缺少必需的参数。 修复并重新提交请求。 在应用程序测试期间应捕获此开发错误。
unauthorized_client 客户端应用程序无法请求授权代码。 客户端应用程序未注册到 Microsoft Entra ID 中或者未添加到用户的 Microsoft Entra 租户时,可能会出现此错误。 应用程序可以提示用户,说明如何安装应用程序并将其添加到 Microsoft Entra ID。
access_denied 资源所有者拒绝许可。 客户端应用程序可以通知用户,除非用户许可,否则无法继续。
unsupported_response_type 授权服务器不支持请求中的响应类型。 修复并重新提交请求。 在应用程序测试期间应捕获此开发错误。
server_error 服务器遇到意外的错误。 重试请求。 这些错误可能是临时状况导致的。 客户端应用程序可向用户说明,其响应由于临时错误而延迟。
temporarily_unavailable 服务器暂时繁忙,无法处理请求。 重试请求。 客户端应用程序可向用户说明,其响应由于临时状况而延迟。
invalid_resource 目标资源无效,原因是它不存在、Microsoft Entra ID 找不到它,或者它未正确配置。 此错误表示未在租户中配置该资源(如果存在)。 应用程序可以提示用户,说明如何安装应用程序并将其添加到 Microsoft Entra ID。

验证 ID 令牌

在应用中接收 ID 令牌不一定足以完全对用户进行身份验证。 你还可能需要验证 ID 令牌的签名并根据应用的要求验证其声明。 与所有 OpenID 提供者一样,Microsoft 标识平台的 ID 令牌是使用公钥加密法签名的 JSON Web 令牌 (JWT)

使用 ID 令牌进行授权的 Web 应用和 Web API 必须验证该令牌,因为此类应用程序会获取对数据的访问权限。 但是,其他类型的应用程序可能无法从 ID 令牌验证中受益。 例如,本机和单页应用 (SPA) 极少能够从 ID 令牌验证中受益,因为任何对设备或浏览器具有物理访问权限的实体都可能会绕过验证。

绕过令牌验证的两个示例是:

  • 通过修改发往设备的网络流量来提供虚假令牌或密钥
  • 在程序执行期间调试应用程序并跳过验证逻辑。

如果你在应用程序中验证 ID 令牌,我们建议不要手动验证。 应该使用令牌验证库来分析和验证令牌。 令牌验证库适用于大多数开发语言、框架和平台。

要在 ID 令牌中验证的内容

除了验证 ID 令牌的签名之外,还应该验证它的多个声明,如验证 ID 令牌中所述。 另请参阅有关签名密钥滚动更新的重要信息

还有其他几种常见验证,具体因应用程序方案而异,包括:

  • 确保用户/组织已注册应用。
  • 确保用户拥有正确的授权/权限
  • 确保进行了一定强度的身份验证,如多重身份验证

验证 ID 令牌后,可以开始与用户建立会话,并使用令牌声明中的信息来个性化应用、显示内容或存储其数据。

协议图:访问令牌获取

许多应用程序不仅需要将用户登录,还需要代表用户访问受保护的资源,例如 Web API。 此方案结合使用 OpenID Connect 和 OAuth 2.0 来分别获取用于验证用户身份的 ID 令牌,和获取受保护资源的访问令牌。

完整的 OpenID Connect 登录和令牌获取流如下图所示:

OpenID Connect protocol: Token acquisition

获取 UserInfo 终结点的访问令牌

除了 ID 令牌之外,OIDC UserInfo 终结点还提供经过身份验证的用户的信息。

若要获取 OIDC UserInfo 终结点的访问令牌,请如下所述修改登录请求:

// Line breaks are for legibility only.

GET https://login.partner.microsoftonline.cn/{tenant}/oauth2/v2.0/authorize?
client_id=535fb089-9ff3-47b6-9bfb-4f1264799865        // Your app registration's Application (client) ID
&response_type=id_token%20token                       // Requests both an ID token and access token
&redirect_uri=http%3A%2F%2Flocalhost%2Fmyapp%2F       // Your application's redirect URI (URL-encoded)
&response_mode=form_post                              // 'form_post' or 'fragment'
&scope=openid+profile+email                           // 'openid' is required; 'profile' and 'email' provide information in the UserInfo endpoint as they do in an ID token. 
&state=12345                                          // Any value - provided by your app
&nonce=678910                                         // Any value - provided by your app

可以使用授权代码流设备代码流刷新令牌代替 response_type=token,以获取应用的访问令牌。

成功的令牌响应

使用 response_mode=form_post 后的成功响应:

POST /myapp/ HTTP/1.1
Host: localhost
Content-Type: application/x-www-form-urlencoded
 access_token=eyJ0eXAiOiJKV1QiLCJub25jZSI6I....
 &token_type=Bearer
 &expires_in=3598
 &scope=email+openid+profile
 &id_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI....
 &state=12345

响应参数的含义是一样的,无论用于获取它们的流是什么。

参数 说明
access_token 将用来调用 UserInfo 终结点的令牌。
token_type 始终为“Bearer”
expires_in 访问令牌的有效期(秒)。
scope 授予访问令牌的权限。 由于 UserInfo 终结点托管在 Microsoft Graph 上,因此 scope 可能包含以前授予应用程序的其他终结点(例如 User.Read)。
id_token 应用请求的 ID 令牌。 可以使用 ID 令牌验证用户的标识,开始与用户建立会话。 可以在 ID 令牌参考中找到有关 ID 令牌及其内容的更多详细信息。
state 如果请求中包含 state 参数,响应中就应该出现相同的值。 应用应该验证请求和响应中的 state 值是否完全相同。

警告

请勿尝试在代码中验证或读取你未拥有的任何 API 的令牌,包括此示例中的令牌。 Microsoft 服务的令牌可以使用将不会作为 JWT 进行验证的特殊格式,还可能会针对使用者(Microsoft 帐户)用户进行加密。 虽然可以通过读取令牌的操作进行调试和学习,但请不要在代码中依赖此操作,也不要假定不是你控制的 API 的令牌的相关具体信息。

错误响应

错误响应还可能发送到重定向 URI,使应用能够相应地处理这些响应:

POST /myapp/ HTTP/1.1
Host: localhost
Content-Type: application/x-www-form-urlencoded

error=access_denied&error_description=the+user+canceled+the+authentication
参数 说明
error 可用于对发生的错误分类以及对错误做出反应的错误代码字符串。
error_description 帮助识别身份验证错误根本原因的特定错误消息。

有关可能的错误代码和建议的客户端操作的说明,请参阅 授权终结点错误的错误代码

获取授权代码和 ID 令牌之后,可将用户登录,并代表用户获取访问令牌。 若要让用户登录,必须验证 ID 令牌,如验证令牌中所述。 若要获取访问令牌,请遵循 OAuth 代码流文档中所述的步骤。

调用 UserInfo 终结点

查看 UserInfo 文档,以了解如何使用此令牌调用 UserInfo 终结点。

发送注销请求

若要注销用户,请执行以下两项操作:

  • 将用户的用户代理重定向到 Microsoft 标识平台的注销 URI
  • 清除应用的 Cookie,或以其他方式结束用户在应用程序中的会话。

如果你无法执行上述任一操作,则用户可能会保持已完成身份验证状态,并且在下次使用你的应用时不会收到登录提示。

如 OpenID Connect 配置文档中所示,将用户代理重定向到 end_session_endpointend_session_endpoint 支持 HTTP GET 和 POST 请求。

GET https://login.partner.microsoftonline.cn/common/oauth2/v2.0/logout?
post_logout_redirect_uri=http%3A%2F%2Flocalhost%2Fmyapp%2F
参数 条件 说明
post_logout_redirect_uri 建议 用户在成功注销后将重定向到的 URL。如果不包括参数,将向用户显示一条 Microsoft 标识平台生成的常规消息。 此 URL 必须与在应用注册门户中为应用程序注册的重定向 URI 之一匹配。
logout_hint 可选 允许在不提示用户选择帐户的情况下注销。 若要使用 logout_hint,请在你的客户端应用程序中启用login_hint可选声明,并将 login_hint 可选声明的值用作 logout_hint 参数。 不要使用 UPN 或电话号码作为 logout_hint 参数的值。

注意

成功注销后,活动会话将设置为非活动。 如果已注销的用户存在有效的主刷新令牌 (PRT),并且执行了新的登录,则 SSO 将中断,并且用户会看到帐户选取器提示。 如果所选选项是引用 PRT 的连接帐户,则会自动继续登录,而无需插入新的凭据。

单一登录

将用户重定向到 end_session_endpoint 时,Microsoft 标识平台将从浏览器中清除用户的会话。 但是,用户可能仍登录到其他使用 Microsoft 帐户进行身份验证的应用程序。 要使这些应用程序能够同时注销用户,Microsoft 标识平台会将 HTTP GET 请求发送到用户当前登录到的所有应用程序的注册 LogoutUrl。 应用程序必须通过清除任何标识用户的会话并返回 200 响应来响应此请求。 如果要在应用程序中支持单一注销,必须在应用程序代码中实现此类 LogoutUrl。 可以从应用注册门户设置 LogoutUrl

后续步骤