Microsoft 标识平台和 OAuth 2.0 隐式授权流

Microsoft 标识平台支持 OAuth 2.0 规范中所述的 OAuth 2.0 隐式授权流。 隐式授权的定义特征是令牌(ID 令牌或访问令牌)直接从 /authorize 终结点返回,而不是从 /token 终结点返回。 这通常在所谓的“混合流”中用作授权代码流的一部分 - 在 /authorize 请求中检索 ID 令牌以及授权代码。

本文介绍如何直接针对应用程序中的协议进行编程,以从 Microsoft Entra ID 请求令牌。 如果可能,建议你改用受支持的 Microsoft 身份验证库 (MSAL) 来获取令牌并调用受保护的 Web API。 另请参阅使用 MSAL 的示例应用

首选授权代码流

由于计划从浏览器中删除第三方 Cookie,因此,隐式授权流不再是合适的身份验证方法。 如果没有第三方 Cookie,隐式流的无提示单一登录 (SSO) 功能将不起作用,导致应用程序在尝试获取新令牌时中断。 强烈建议你让所有新的应用程序都使用授权代码流(现在替代隐式流来支持单页应用)。 现有单页应用还应迁移到授权代码流

OAuth2 隐式授权的适用方案

只有对于登录流的初始交互式部分(其中缺少第三方 Cookie 不会影响你的应用程序),隐式授权才是可靠的。 此限制意味着,你只应将其用作混合流的一部分,在混合流中,你的应用程序从授权终结点请求代码和令牌。 在混合流中,应用程序会收到可兑换为刷新令牌的代码,从而确保应用的登录会话在一段时间内保持有效。

协议图

下图显示了整个隐式登录流的样子,后续各部分详细介绍了每个步骤。

Diagram showing the implicit sign-in flow

发送登录请求

最初将用户登录到应用时,可以发送 OpenID Connect 身份验证请求,并从 Microsoft 标识平台获取 id_token

重要

若要成功请求 ID 令牌和/或访问令牌,必须通过在“隐式授权和混合流”部分选择“ID 令牌”和“访问令牌”,为 Microsoft Entra 管理中心 - 应用注册页中的应用注册启用相应的隐式授权流。 如果未启用,将返回 unsupported_response 错误:

The provided value for the input parameter 'response_type' is not allowed for this client. Expected value is 'code'

// Line breaks for legibility only

https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize?
client_id=535fb089-9ff3-47b6-9bfb-4f1264799865
&response_type=id_token
&redirect_uri=http%3A%2F%2Flocalhost%2Fmyapp%2F
&scope=openid
&response_mode=fragment
&state=12345
&nonce=678910
参数 类型 描述
tenant 必需 请求路径中的 {tenant} 值可用于控制哪些用户可以登录应用程序。 可以使用的值包括 commonorganizationsconsumers 和租户标识符。 有关详细信息,请参阅协议基础知识。需要注意的是,对于将用户从一个租户登录到另一个租户的来宾方案,必须提供租户标识符才能将其正确登录到资源租户。
client_id (必需) Microsoft Entra 管理中心 - 应用注册页分配给应用的“应用程序(客户端) ID”。
response_type (必需) 必须包含 OpenID Connect 登录的 id_token 。 也可以包含 response_typetoken。 此处使用 token ,让应用能够立即从授权终结点接收访问令牌,而无需向授权终结点发出第二次请求。 如果使用 token response_type,scope 参数必须包含范围,以指出要对哪个资源(例如,Microsoft Graph 上的 user.read)发出令牌。 它还可以包含可提供授权代码的 code(取代 token),该授权代码在授权代码流中使用。 此 id_token+code 响应有时称为混合流。
redirect_uri 建议 应用的重定向 URI,应用可在其中发送和接收身份验证响应。 其必须完全符合在门户中注册的其中一个重定向 URI,否则必须是编码的 URL。
scope (必需) 范围的空格分隔列表。 针对 OpenID Connect (id_tokens),即必须包含范围 openid,其在同意 UI 中转换为“你将登录”权限。 或者,也可以包含 emailprofile 范围,以获取对其他用户数据的访问权限。 也可以在此请求中包含其他范围,以请求对各种资源的许可(如果请求了访问令牌)。
response_mode 可选 指定将生成的令牌送回到应用程序时应该使用的方法。 默认为仅查询访问令牌,但如果请求包括 id_token,则会进行分段。
state 建议 同样随令牌响应返回的请求中所包含的值。 可以是想要的任何内容的字符串。 随机生成的唯一值通常用于 防止跨站点请求伪造攻击。 该 state 也用于在身份验证请求出现之前,于应用中编码用户的状态信息,例如之前所在的网页或视图。
nonce (必需) 由应用程序生成且包含在请求中的值,以声明方式包含在生成的 ID 令牌中。 应用程序接着便可确认此值,以减少令牌重新执行攻击。 该值通常是随机的唯一字符串,可用于标识请求的来源。 只有请求 id_token 时才是必需的。
prompt 可选 表示需要的用户交互类型。 此时唯一有效的值为 loginnoneselect_accountconsentprompt=login 将强制用户在该请求上输入凭据,取消单一登录。 prompt=none 则相反 - 它确保不对用户显示任何交互式提示。 如果无法在无提示的情况下通过 SSO 完成请求,Microsoft 标识平台将返回一个错误。 prompt=select_account 将用户发送到一个帐户选取器,其中将显示在会话中记住的所有帐户。 prompt=consent 会在用户登录之后触发 OAuth 同意对话框,要求用户向应用授予权限。
login_hint 可选 如果事先知道用户名,可以使用此参数预先填充用户登录页的用户名/电子邮件地址字段。 通常,应用在已经从前次登录提取 login_hint可选声明后,会在重新身份验证时使用此参数。
domain_hint 可选 如果包含,它跳过用户在登录页上经历的基于电子邮件的发现过程,导致稍微更加流畅的用户体验。 此参数通常用于在单个租户中运行的业务线应用,它们会提供给定租户中的域名,将用户转发给该租户的联合身份验证提供程序。 此提示会阻止来宾登录到此应用程序,并限制 FIDO 之类的云凭据的使用。

此时,将请求用户输入凭据并完成身份验证。 Microsoft 标识平台还会确保用户已许可 scope 查询参数中指定的权限。 如果用户未曾同意这些权限的任何一项,就请求用户同意请求的权限。 有关详细信息,请参阅权限、同意和多租户应用

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

成功的响应

使用 response_mode=fragmentresponse_type=id_token+code 的成功响应如下所示(为方便阅读,包含了换行符):

GET https://localhost/myapp/#
code=0.AgAAktYV-sfpYESnQynylW_UKZmH-C9y_G1A
&id_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5HVEZ2ZEstZnl0aEV1Q...
&state=12345
参数 说明
code 如果 response_type 包含 code,则包含该参数。 它是适合在授权代码流中使用的授权代码。
access_token 如果 response_type 包含 token,则包含该参数。 应用请求的访问令牌。 访问令牌不得进行解码或检查,应当作为不透明字符串对待。
token_type 如果 response_type 包含 token,则包含该参数。 这将始终为 Bearer
expires_in 如果 response_type 包含 token,则包含该参数。 表示令牌有效的秒数(针对缓存目的)。
scope 如果 response_type 包含 token,则包含该参数。 表示 access_token 的有效范围。 可能不包括所有请求的范围(如果它们不适用于用户)。 例如,使用个人帐户登录时请求的仅 Microsoft Entra 范围。
id_token 有符号 JSON Web 令牌 (JWT)。 应用可以解码此令牌的段,以请求有关登录用户的信息。 应用可以缓存并显示这些值,但不应依赖它们来获取任何授权或安全边界。 有关 ID 令牌的详细信息,请参阅 id_token reference
注意: 仅当请求了 openid 作用域且 response_type 包括 id_tokens 时才提供。
state 如果请求中包含 state 参数,响应中就应该出现相同的值。 应用应该验证请求和响应中的 state 值是否完全相同。

警告

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

错误响应

错误响应可能也发送到 redirect_uri ,让应用可以适当地处理:

GET https://localhost/myapp/#
error=access_denied
&error_description=the+user+canceled+the+authentication
参数 说明
error 可用于分类发生的错误类型与响应错误的错误码字符串。
error_description 帮助开发人员识别身份验证错误根本原因的特定错误消息。

以无提示方式获取访问令牌

重要

隐式流的这一部分不太可能适用于你的应用程序,因为默认情况下删除了第三方 Cookie,所以它用于各种浏览器中。 虽然目前这仍然适用于不在 Incognito 中的基于 Chromium 的浏览器,但开发人员应该重新考虑这部分流的使用。 在不支持第三方 Cookie 的浏览器中,你会收到一条错误消息,指出没有用户登录,因为浏览器删除了登录页面的会话 Cookie。

已经将用户登录到单页应用,现在可以通过无提示方式获取访问令牌以调用受到 Microsoft 标识平台保护的 Web API,例如 Microsoft Graph。 即使已使用 token response_type 收到令牌,仍然可以使用此方法获取其他资源的令牌,而无需再次将用户重定向到登录页。

在正常的 OpenID Connect/OAuth 流中,可以通过对 Microsoft 标识平台 /token 终结点进行请求来实现此目的。 你可以在隐藏的 iframe 中进行请求,以获取其他 Web API 的新令牌:

// Line breaks for legibility only

https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize?
client_id=535fb089-9ff3-47b6-9bfb-4f1264799865
&response_type=token
&redirect_uri=http%3A%2F%2Flocalhost%2Fmyapp%2F
&scope=https%3A%2F%2Fgraph.microsoft.com%2Fuser.read
&response_mode=fragment
&state=12345
&nonce=678910
&prompt=none
&login_hint=myuser@mycompany.com

有关 URL 中的查询参数的详细信息,请参阅发送登录请求

提示

请尝试将以下请求复制并粘贴到浏览器选项卡中! (不要忘记使用适用于用户的正确值替换 login_hint 值)

https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=535fb089-9ff3-47b6-9bfb-4f1264799865&response_type=token&redirect_uri=http%3A%2F%2Flocalhost%2Fmyapp%2F&scope=https%3A%2F%2Fgraph.microsoft.com%2Fuser.read&response_mode=fragment&state=12345&nonce=678910&prompt=none&login_hint={your-username}

请注意,即使是在不支持第三方 Cookie 的浏览器中,也可以这样做,因为你要将其直接输入到浏览器栏中,而不是在 iframe 中打开它。

借助 prompt=none 参数,此请求立即成功或立即失败,并返回应用程序。 响应会通过 response_mode 参数中指定的方法,发送到位于所指示的 redirect_uri 的应用。

成功的响应

使用 response_mode=fragment 的成功响应如下所示:

GET https://localhost/myapp/#
access_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5HVEZ2ZEstZnl0aEV1Q...
&state=12345
&token_type=Bearer
&expires_in=3599
&scope=https%3A%2F%2Fgraph.microsoft.com%2Fdirectory.read
参数 说明
access_token 如果 response_type 包含 token,则包含该参数。 应用请求的访问令牌,在本例中为 Microsoft Graph 的访问令牌。 访问令牌不得进行解码或检查,应当作为不透明字符串对待。
token_type 这将始终为 Bearer
expires_in 表示令牌有效的秒数(针对缓存目的)。
scope 表示访问令牌的有效范围。 如果请求的范围不适用于用户,可能不包括所有请求的范围(在使用个人帐户登录时请求仅 Microsoft Entra 范围的情况下)。
id_token 有符号 JSON Web 令牌 (JWT)。 如果 response_type 包含 id_token,则包含该参数。 应用可以解码此令牌的段,以请求有关登录用户的信息。 应用可以缓存并显示这些值,但不应依赖它们来获取任何授权或安全边界。 有关 id_tokens 的详细信息,请参阅id_token参考
注意: 仅当已请求 openid 作用域时提供。
state 如果请求中包含 state 参数,响应中就应该出现相同的值。 应用应该验证请求和响应中的 state 值是否完全相同。

错误响应

错误响应还可能发送到 redirect_uri ,因此应用可以适当地对其进行处理。 如果是 prompt=none,预期的错误为:

GET https://localhost/myapp/#
error=user_authentication_required
&error_description=the+request+could+not+be+completed+silently
参数 说明
error 可用于分类发生的错误类型与响应错误的错误码字符串。
error_description 帮助开发人员识别身份验证错误根本原因的特定错误消息。

如果在 iframe 请求中收到此错误,用户必须再次以交互方式登录以检索新令牌。 可以选择对应用程序合理的任何方式处理这种情况。

刷新令牌

隐式授权不提供刷新令牌。 ID 令牌和访问令牌很快就会过期,因此应用必须准备好定期刷新这些令牌。 若要刷新任一类型的令牌,可以通过使用 prompt=none 参数控制 Microsoft 标识平台的行为,来执行上述同一隐藏的 iframe 请求。 若要接收新的 ID 令牌,请务必使用 response_typescope=openid 中的 id_token,以及 nonce 参数。

在不支持第三方 Cookie 的浏览器中,这会导致错误,指出没有用户登录。

发送注销请求

OpenID Connect end_session_endpoint 允许应用向 Microsoft 标识平台发送请求以结束用户的会话,并清除 Microsoft 标识平台设置的 Cookie。 要完全将用户从 Web 应用程序注销,应用应结束自己与用户的会话(通常通过清除令牌缓存或删除 Cookie 实现),然后将浏览器重定向到:

https://login.microsoftonline.com/{tenant}/oauth2/v2.0/logout?post_logout_redirect_uri=https://localhost/myapp/
参数 类型 描述
tenant 必需 请求路径中的 {tenant} 值可用于控制哪些用户可以登录应用程序。 可以使用的值包括 commonorganizationsconsumers 和租户标识符。 有关详细信息,请参阅协议基础知识
post_logout_redirect_uri 建议 注销完成后用户应返回到的 URL。 此值必须与为应用程序注册的重定向 URI 之一匹配。 如果未包含,Microsoft 标识平台会向用户显示一条常规消息。

后续步骤