适用于电视和受限输入设备应用的 OAuth 2.0

<ph type="x-smartling-placeholder"></ph>

本文档介绍了如何通过在电视、游戏机和打印机等设备上运行的应用实现 OAuth 2.0 授权来访问 Google API。更具体地说,此流程是专为无权访问浏览器或输入功能有限的设备而设计的。

OAuth 2.0 可让用户与应用共享特定数据,同时保持其用户名、密码和其他信息的私密性。例如,电视应用可以使用 OAuth 2.0 获取权限,以选择存储在 Google 云端硬盘中的文件。

由于使用此流程的应用分发到各个设备,因此假定这些应用不能保密。当用户访问应用或正在后台运行时,用户可以访问 Google API。

替代方案

如果您正在为 Android、iOS、macOS、Linux 或 Windows(包括通用 Windows 平台)平台编写应用,且该应用具有访问浏览器和完整输入功能的权限,请使用适用于移动应用和桌面应用的 OAuth 2.0 流程。(即使您的应用是不带图形界面的命令行工具,您也应使用该流程。)

如果您只想让用户使用其 Google 帐号登录,并使用 JWT ID 令牌获取基本用户个人资料信息,请参阅在电视和受限输入设备上登录

前提条件

为您的项目启用 API

任何调用 Google API 的应用都需要在 API Console中启用这些 API。

如需为您的项目启用该 API,请按以下步骤操作:

  1. Open the API Library (在 Google API Console中)。
  2. If prompted, select a project, or create a new one.
  3. API Library 列出了所有可用的 API(按产品系列和热门程度分组)。如果您要启用的 API 在列表中不可见,请使用搜索功能查找该 API,或点击该 API 所属产品系列中的查看全部
  4. 选择您要启用的 API,然后点击启用按钮。
  5. If prompted, enable billing.
  6. If prompted, read and accept the API's Terms of Service.

创建授权凭据

任何使用 OAuth 2.0 访问 Google API 的应用都必须使用授权凭据向 Google 的 OAuth 2.0 服务器标识相应应用。以下步骤说明了如何为项目创建凭据。然后,您的应用可以使用这些凭据访问您为该项目启用的 API。

  1. Go to the Credentials page.
  2. 依次点击创建凭据 > OAuth 客户端 ID
  3. 选择电视和受限输入设备应用类型。
  4. 为您的 OAuth 2.0 客户端命名,然后点击创建

确定访问权限范围

借助范围,您的应用可以仅请求访问所需的资源,同时还能控制用户向您的应用授予的访问权限。因此,请求的范围数量与征得用户同意的可能性之间是反向的。

在开始实现 OAuth 2.0 授权之前,我们建议您确定您的应用需要访问的权限范围。

查看已安装应用或设备的允许的范围列表。

获取 OAuth 2.0 访问令牌

即使您的应用在输入功能有限的设备上运行,用户也必须拥有对输入功能更丰富的设备的单独访问权限才能完成此授权流程。该流程包含以下步骤:

  1. 您的应用会向 Google 的授权服务器发送请求,以标识您的应用将请求访问权限的范围。
  2. 服务器会返回在后续步骤中使用的若干信息,如设备代码和用户代码。
  3. 您可以显示信息,供用户在单独的设备上授权您的应用。
  4. 您的应用开始轮询 Google 的授权服务器以确定用户是否已授权您的应用。
  5. 用户切换到具有更丰富的输入功能的设备,启动网络浏览器,转到第 3 步中显示的网址,并输入同样在第 3 步中显示的代码。用户随后可以授予(或拒绝)您的应用访问权限。
  6. 对您的轮询请求的下一个响应包含您的应用代表用户对请求进行授权所需的令牌。(如果用户拒绝访问您的应用,则响应将不包含令牌。)

下图说明了此过程:

用户在具有浏览器的独立设备上登录

下面几部分将详细介绍这些步骤。考虑到设备可能具备的功能和运行时环境的范围,本文档中显示的示例使用 curl 命令行实用程序。这些示例应可以轻松移植到各种语言和运行时。

第 1 步:请求设备和用户代码

在此步骤中,您的设备将向 Google 的授权服务器 (https://oauth2.googleapis.com/device/code) 发送一个 HTTP POST 请求,以标识您的应用以及您的应用希望代表用户访问的访问权限范围。 您应使用 device_authorization_endpoint 元数据值从发现文档中检索此网址。添加以下 HTTP 请求参数:

参数
client_id 必需

您的应用的客户端 ID。您可以在 API ConsoleCredentials page中找到此值。

scope 必需

用空格分隔的范围列表,用于标识您的应用可以代表用户访问的资源。这些值会告知用户 Google 向用户显示的同意屏幕。对于已安装的应用或设备,请参阅允许的范围列表。

通过范围,您的应用可以仅请求访问所需的资源,同时还能控制用户向您的应用授予的访问权限。因此,请求的范围数量与征得用户同意的可能性之间是相反的。

示例

以下代码段显示了一个示例请求:

POST /device/code HTTP/1.1
Host: oauth2.googleapis.com
Content-Type: application/x-www-form-urlencoded

client_id=client_id&scope=email%20profile

以下示例展示了用于发送同一请求的 curl 命令:

curl -d "client_id=client_id&scope=email%20profile" \
     https://oauth2.googleapis.com/device/code

第 2 步:处理授权服务器响应

授权服务器将返回以下响应之一:

成功响应

如果请求有效,则响应将是包含以下属性的 JSON 对象:

属性
device_code Google 唯一标识的值,用于识别运行请求授权的设备。用户将通过另一部功能更丰富的设备授权该设备。例如,用户可能会使用笔记本电脑或手机向在电视上运行的应用授权。在本例中,device_code 用于标识电视。

通过此代码,运行应用的设备可以安全地确定用户是否已授予访问权限。

expires_in device_codeuser_code 的有效时长(以秒为单位)。届时,如果用户没有完成授权流程,而您的设备也没有轮询以检索有关用户决定的信息,那么您可能需要从第 1 步重新开始此过程。
interval 您的设备两次轮询请求之间相隔的时长(以秒为单位)。例如,如果值为 5,您的设备应每 5 秒向 Google 的授权服务器发送一次轮询请求。如需了解详情,请参阅第 3 步
user_code 区分大小写的值,用于标识 Google 请求应用访问的范围。您的界面会指示用户在具有更丰富输入功能的单独设备上输入此值。然后,Google 会在请求用户授予应用访问权限时,使用该值显示一组正确的范围。
verification_url 用户在单独的设备上必须导航到的网址才能进入 user_code,然后授予或拒绝应用的访问权限。您的界面还会显示此值。

以下代码段显示了一个响应示例:

{
  "device_code": "4/4-GMMhmHCXhWEzkobqIHGG_EnNYYsAkukHspeYUk9E8",
  "user_code": "GQVQ-JKEC",
  "verification_url": "https://www.google.com/device",
  "expires_in": 1800,
  "interval": 5
}

已超出配额响应

如果您的设备代码请求超出了与您的客户端 ID 关联的配额,您会收到 403 响应,其中包含以下错误:

{
  "error_code": "rate_limit_exceeded"
}

在这种情况下,请使用退避策略来降低请求速率。

第 3 步:显示用户代码

向用户显示您在第 2 步中获取的 verification_urluser_code。这两个值都可以包含 US-ASCII 字符集中的任何可打印字符。您向用户显示的内容应指示用户在单独的设备上转到 verification_url,然后输入 user_code

在设计界面时,请谨记以下规则:

  • user_code
    • user_code 必须显示在可以处理 15 个字符的 字段中。换言之,如果您可以正确显示代码 WWWWWWWWWWWWWWW,您的界面是有效的,我们建议您在测试 user_code 的界面显示方式时使用以下字符串值。
    • user_code 区分大小写,不应以任何方式修改,例如更改大小写或插入其他格式字符。
  • verification_url
    • 显示 verification_url 的空间必须足够宽,可以处理长度为 40 个字符的网址字符串。
    • 您不应以任何方式修改 verification_url,但可以选择移除要显示的方案。如果您确实计划出于显示原因从网址中去除架构(例如 https://),请确保您的应用可以同时处理 httphttps 变体。

第 4 步:轮询 Google 的授权服务器

由于用户将使用其他设备导航到 verification_url 并授予(或拒绝)访问权限,因此当用户响应访问请求时,请求设备不会自动收到通知。因此,发出请求的设备需要轮询 Google 的授权服务器,以确定用户何时响应了请求。

发出请求的设备应继续发送轮询请求,直到收到表明用户已响应访问请求的响应,或第 2 步获得的 device_codeuser_code 到期为止。第 2 步中返回的 interval 用于指定请求之间的等待时间(以秒为单位)。

要轮询的端点的网址为 https://oauth2.googleapis.com/token。轮询请求包含以下参数:

参数
client_id 您的应用的客户端 ID。您可以在 API ConsoleCredentials page中找到此值。
client_secret 所提供 client_id 的客户端密钥。您可以在 API ConsoleCredentials page中找到此值。
device_code 第 2 步中的授权服务器返回的 device_code
grant_type 并将此值设为 urn:ietf:params:oauth:grant-type:device_code

示例

以下代码段显示了一个示例请求:

POST /token HTTP/1.1
Host: oauth2.googleapis.com
Content-Type: application/x-www-form-urlencoded

client_id=client_id&
client_secret=client_secret&
device_code=device_code&
grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Adevice_code

以下示例展示了用于发送同一请求的 curl 命令:

curl -d "client_id=client_id&client_secret=client_secret& \
         device_code=device_code& \
         grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Adevice_code" \
         -H "Content-Type: application/x-www-form-urlencoded" \
         /token

第 5 步:用户响应访问权限请求

下图显示的页面类似于用户在导航到您在第 3 步中显示的 verification_url 时会看到的页面:

通过输入验证码连接设备

在进入 user_code 后,如果尚未登录 Google,则在登录 Google 后,用户会看到如下所示的同意屏幕:

设备客户端的同意屏幕示例

第 6 步:处理轮询请求的响应

Google 的授权服务器通过以下响应之一响应每个轮询请求:

已授予访问权限

如果用户向设备授予访问权限(通过在同意屏幕上点击 Allow),响应会包含访问令牌和刷新令牌。这些令牌使您的设备能够代表用户访问 Google API。(响应中的 scope 属性决定了设备可以访问哪些 API。)

在这种情况下,API 响应包含以下字段:

字段
access_token 您的应用发送的用于授权 Google API 请求的令牌。
expires_in 访问令牌的剩余生命周期(以秒为单位)。
refresh_token 可用于获取新访问令牌的令牌。除非用户撤消访问权限,否则刷新令牌会一直有效。 请注意,系统会始终为设备返回刷新令牌。
scope access_token 授予的访问权限范围,表示为以空格分隔且区分大小写的字符串列表。
token_type 返回的令牌类型。此时,此字段的值始终设置为 Bearer

以下代码段显示了一个响应示例:

{
  "access_token": "1/fFAGRNJru1FTz70BzhT3Zg",
  "expires_in": 3920,
  "scope": "openid https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email",
  "token_type": "Bearer",
  "refresh_token": "1/xEoDL4iW3cxlI7yDbSRFYNG01kVKM2C-259HOF2aQbI"
}

访问令牌的生命周期有限。如果您的应用需要访问很长一段时间的某个 API,它可以使用刷新令牌来获取新的访问令牌。如果您的应用需要此类访问权限,则应存储刷新令牌以供日后使用。

访问遭拒

如果用户拒绝授予对设备的访问权限,则服务器响应会有 403 HTTP 响应状态代码 (Forbidden)。该响应包含以下错误:

{
  "error": "access_denied",
  "error_description": "Forbidden"
}

待授权

如果用户尚未完成授权流程,则服务器会返回 428 HTTP 响应状态代码 (Precondition Required)。该响应包含以下错误:

{
  "error": "authorization_pending",
  "error_description": "Precondition Required"
}

轮询过于频繁

如果设备过于频繁地发送轮询请求,服务器会返回 403 HTTP 响应状态代码 (Forbidden)。该响应包含以下错误:

{
  "error": "slow_down",
  "error_description": "Forbidden"
}

其他错误

如果轮询请求缺少任何必需的参数,或者参数值不正确,则授权服务器也会返回错误。这些请求通常包含 400 (Bad Request) 或 401 (Unauthorized) HTTP 响应状态代码。这些错误包括:

错误 HTTP 状态代码 说明
invalid_client 401 找不到 OAuth 客户端。例如,如果 client_id 参数值无效,则会出现此错误。
invalid_grant 400 code 参数值无效。
unsupported_grant_type 400 grant_type 参数值无效。

调用 Google API

在您的应用获得访问令牌后,您可以使用该令牌代表指定的用户帐号调用 Google API(如果已授予 API 所需的访问权限范围)。为此,您可以在向 API 发出的请求中添加访问令牌,具体方法是包含 access_token 查询参数或 Authorization HTTP 标头 Bearer 值。请尽可能使用 HTTP 标头,因为查询字符串通常会显示在服务器日志中。在大多数情况下,您可以使用客户端库设置对 Google API 的调用(例如,调用 Drive Files API 时)。

您可以访问 OAuth 2.0 Playground,试用所有 Google API 并查看其范围。

HTTP GET 示例

使用 Authorization: Bearer HTTP 标头调用 drive.files 端点 (Drive Files API) 的方式可能如下所示。请注意,您需要指定自己的访问令牌:

GET /drive/v2/files HTTP/1.1
Host: www.googleapis.com
Authorization: Bearer access_token

下面是使用 access_token 查询字符串参数,对经过身份验证的用户调用的同一 API:

GET https://www.googleapis.com/drive/v2/files?access_token=access_token

curl 示例

您可以使用 curl 命令行应用测试这些命令。下面是一个使用 HTTP 标头选项(首选)的示例:

curl -H "Authorization: Bearer access_token" https://www.googleapis.com/drive/v2/files

或者,查询字符串参数选项:

curl https://www.googleapis.com/drive/v2/files?access_token=access_token

刷新访问令牌

访问令牌会定期过期,并在相关 API 请求中失效。如果您已请求离线访问与令牌关联的范围,则可以刷新访问令牌,而无需提示用户授予权限(包括当用户不在时)。

如需刷新访问令牌,您的应用会向 Google 的授权服务器 (https://oauth2.googleapis.com/token) 发送 HTTPS POST 请求,其中包含以下参数:

字段
client_id 通过 API Console获取的客户端 ID。
client_secret API Console获得的客户端密钥。
grant_type 根据 OAuth 2.0 规范中的定义,此字段的值必须设置为 refresh_token
refresh_token 从授权代码交换中返回的刷新令牌。

以下代码段显示了一个示例请求:

POST /token HTTP/1.1
Host: oauth2.googleapis.com
Content-Type: application/x-www-form-urlencoded

client_id=your_client_id&
client_secret=your_client_secret&
refresh_token=refresh_token&
grant_type=refresh_token

只要用户尚未撤消授予应用的访问权限,令牌服务器就会返回一个包含新访问令牌的 JSON 对象。以下代码段显示了一个响应示例:

{
  "access_token": "1/fFAGRNJru1FTz70BzhT3Zg",
  "expires_in": 3920,
  "scope": "https://www.googleapis.com/auth/drive.metadata.readonly",
  "token_type": "Bearer"
}

请注意,系统将会颁发的刷新令牌数量是有限制的:每个客户端/用户组合一个限制,所有客户端上每位用户都另一个限制。您应将刷新令牌保存在长期存储空间中,并使其一直有效。如果您的应用请求刷新令牌的次数过多,则可能会遇到这些限制,在这种情况下,较旧的刷新令牌将停止运行。

撤消令牌

在某些情况下,用户可能会想要撤消对应用的访问权限。用户可以通过访问帐号设置撤消访问权限。如需了解详情,请参阅撤消有权访问您帐号的第三方网站或应用的应用部分的支持文档。

应用还可以以编程方式撤消授予它的访问权限。在用户退订、移除应用或应用所需的 API 资源发生了显著变化的情况下,程序化撤消非常重要。也就是说,移除流程的一部分可能包含 API 请求,以确保移除之前授予应用的权限。

为了以编程方式撤消令牌,您的应用会向 https://oauth2.googleapis.com/revoke 发出请求,并将令牌添加为参数:

curl -d -X -POST --header "Content-type:application/x-www-form-urlencoded" \
        https://oauth2.googleapis.com/revoke?token={token}

该令牌可以是访问令牌,也可以是刷新令牌。如果此令牌是访问令牌且具有相应的刷新令牌,则此刷新令牌也会被撤消。

如果吊销成功处理,则响应的 HTTP 状态代码为 200。对于错误情况,系统会返回 HTTP 状态代码 400 以及错误代码。

允许的范围

只有以下范围支持设备的 OAuth 2.0 流程:

OpenID ConnectGoogle 登录

  • email
  • openid
  • profile

Drive API

  • https://www.googleapis.com/auth/drive.appdata
  • https://www.googleapis.com/auth/drive.file

YouTube API

  • https://www.googleapis.com/auth/youtube
  • https://www.googleapis.com/auth/youtube.readonly