Microsoft 标识平台和 OAuth 2.0 设备权限授予流
Microsoft 标识平台支持设备权限授予,用户可以登录到智能电视、IoT 设备或打印机等输入受限设备。 为启用此流,设备会让用户在另一台设备上的浏览器中访问一个网页,以进行登录。 用户登录后,设备可以获取所需的访问令牌和刷新令牌。
本文介绍如何在应用程序中直接针对协议进行编程。 如果可能,建议你改用受支持的 Microsoft 身份验证库 (MSAL) 来获取令牌并调用受保护的 Web API。 有关示例,请参阅使用 MSAL 的示例应用。
协议图
下图显示了整个设备代码流。 本文解释了每个步骤。
设备授权请求
客户端必须先在身份验证服务器中检查用于发起身份验证的设备和用户代码。 客户端从 /devicecode
终结点收集此请求。 在此请求中,客户端也应包含需要从用户获取的权限。
发送请求后,用户有 15 分钟的时间登录。 这是 expires_in
的默认值。 仅当用户表示已准备好登录时,才应发出请求。
// Line breaks are for legibility only.
POST https://login.microsoftonline.com/{tenant}/oauth2/v2.0/devicecode
Content-Type: application/x-www-form-urlencoded
client_id=00001111-aaaa-2222-bbbb-3333cccc4444
&scope=user.read%20openid%20profile
参数 | 条件 | 描述 |
---|---|---|
tenant |
必须 | 可以是 /common 、/consumers 或 /organizations 。 它也可以是要以 GUID 或友好名称格式向其请求权限的目录租户。 |
client_id |
必须 | Microsoft Entra 管理中心 - 应用注册体验分配给应用的“应用程序(客户端) ID”。 |
scope |
必须 | 希望用户同意的范围的空格分隔列表。 |
设备授权响应
成功的响应是一个 JSON 对象,其中包含允许用户登录所需的信息。
参数 | 格式 | 说明 |
---|---|---|
device_code |
String | 一个长字符串,用于验证客户端与授权服务器之间的会话。 客户端使用此参数从授权服务器请求访问令牌。 |
user_code |
字符串 | 向用户显示的短字符串,用于标识辅助设备上的会话。 |
verification_uri |
URI | 用户在登录时应使用 user_code 转到的 URI。 |
expires_in |
int | device_code 和 user_code 过期之前的秒数。 |
interval |
int | 在发出下一个轮询请求之前客户端应等待的秒数。 |
message |
String | 用户可读的字符串,包含面向用户的说明。 可以通过在请求中包含 ?mkt=xx-XX 格式的查询参数并填充相应的语言区域性代码,将此字符串本地化。 |
注意
此时不包括或不支持 verification_uri_complete
响应字段。 我们提到这一点是因为如果你阅读标准,你会看到 verification_uri_complete
作为设备代码流标准的可选部分列出。
对用户进行身份验证
客户端收到 user_code
和 verification_uri
后,系统会显示值,并引导用户通过移动设备或电脑上的浏览器登录。
如果用户使用个人帐户,借助 /common
或 /consumers
进行身份验证,则系统会要求其再次登录,以便将身份验证状态转移到设备。 这是因为该设备无法访问用户的 Cookie。 他们需要同意客户端请求的权限。 但是,这不适合用于身份验证的工作或学校帐户。
尽管用户是在 verification_uri
中进行身份验证,但客户端应使用 device_code
来轮询所请求令牌的 /token
终结点。
POST https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded
grant_type=urn:ietf:params:oauth:grant-type:device_code&client_id=00001111-aaaa-2222-bbbb-3333cccc4444&device_code=GMMhmHCXhWEzkobqIHGG_EnNYYsAkukHspeYUk9E8...
参数 | 必需 | 说明 |
---|---|---|
tenant |
必须 | 初始请求中使用的同一租户或租户别名。 |
grant_type |
必须 | 必须是 urn:ietf:params:oauth:grant-type:device_code |
client_id |
必须 | 必须与初始请求中使用的 client_id 匹配。 |
device_code |
必须 | 设备授权请求中返回的 device_code 。 |
预期错误
设备代码流是轮询协议,因此必须预估用户身份验证完成之前向客户端提供的错误信息。
错误 | 说明 | 客户端操作 |
---|---|---|
authorization_pending |
用户尚未完成身份验证,但未取消流。 | 在至少 interval 秒之后重复请求。 |
authorization_declined |
最终用户拒绝了授权请求。 | 停止轮询,并恢复到未经过身份验证状态。 |
bad_verification_code |
未识别已发送到 /token 终结点的 device_code 。 |
验证客户端是否在请求中发送了正确的 device_code 。 |
expired_token |
已超过值 expires_in ,因此无法再使用 device_code 进行身份验证。 |
停止轮询,并恢复到未经过身份验证状态。 |
成功的身份验证响应
成功的令牌响应如下所示:
{
"token_type": "Bearer",
"scope": "User.Read profile openid email",
"expires_in": 3599,
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5HVEZ2ZEstZnl0aEV1Q...",
"refresh_token": "AwABAAAAvPM1KaPlrEqdFSBzjqfTGAMxZGUTdM0t4B4...",
"id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJhdWQiOiIyZDRkMTFhMi1mODE0LTQ2YTctOD..."
}
参数 | 格式 | 说明 |
---|---|---|
token_type |
字符串 | 总是为 Bearer 。 |
scope |
空格分隔的字符串 | 如果返回访问令牌,则会列出该访问令牌的有效范围。 |
expires_in |
int | 包含的访问令牌有效的秒数。 |
access_token |
不透明字符串 | 针对请求的范围颁发。 |
id_token |
JWT | 如果原始 scope 参数包含 openid 范围,则颁发。 |
refresh_token |
不透明字符串 | 如果原始 scope 参数包含 offline_access ,则颁发。 |
可以运行 OAuth 代码流文档中所述的同一个流,使用刷新令牌来获取新的访问令牌和刷新令牌。
警告
请勿尝试在代码中验证或读取你未拥有的任何 API 的令牌,包括此示例中的令牌。 Microsoft 服务的令牌可以使用将不会作为 JWT 进行验证的特殊格式,还可能会针对使用者(Microsoft 帐户)用户进行加密。 虽然可以通过读取令牌的操作进行调试和学习,但请不要在代码中依赖此操作,也不要假定不是你控制的 API 的令牌的相关具体信息。