用于电视和有限输入设备应用程序的 OAuth 2.0

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

OAuth 2.0 允许用户与应用程序共享特定数据,同时保持用户名、密码和其他信息的私密性。例如,电视应用程序可以使用 OAuth 2.0 来获得选择存储在 Google Drive 上的文件的权限。

由于使用此流程的应用程序分布在各个设备上,因此假定应用程序无法保密。当用户在应用程序中或应用程序在后台运行时,他们可以访问 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 在列表中不可见,请使用搜索找到它,或单击它所属的产品系列中的查看全部
  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 客户端并单击Create

确定访问范围

范围使您的应用程序仅请求访问它需要的资源,同时还使用户能够控制他们授予您的应用程序的访问量。因此,请求范围的数量与获得用户同意的可能性之间可能存在反比关系。

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

请参阅已安装应用程序或设备的允许范围列表。

获取 OAuth 2.0 访问令牌

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

  1. 您的应用程序向 Google 的授权服务器发送一个请求,该请求确定您的应用程序将请求访问权限的范围。
  2. 服务器以在后续步骤中使用的几条信息进行响应,例如设备代码和用户代码。
  3. 您显示用户可以在单独的设备上输入的信息以授权您的应用程序。
  4. 您的应用程序开始轮询 Google 的授权服务器,以确定用户是否已授权您的应用程序。
  5. 用户切换到具有更丰富输入功能的设备,启动 Web 浏览器,导航到步骤 3 中显示的 URL 并输入步骤 3 中也显示的代码。然后用户可以授予(或拒绝)对您的应用程序的访问权限。
  6. 对您的轮询请求的下一个响应包含您的应用代表用户授权请求所需的令牌。 (如果用户拒绝访问您的应用程序,则响应不包含令牌。)

下图说明了这个过程:

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

以下部分详细解释了这些步骤。鉴于设备可能具有的功能范围和运行时环境,本文档中显示的示例使用curl命令行实用程序。这些示例应该很容易移植到各种语言和运行时。

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

在此步骤中,您的设备会向 Google 的授权服务器(位于https://oauth2.googleapis.com/device/code )发送一个 HTTP POST 请求,该请求标识您的应用程序以及您的应用程序想要在用户的访问权限上访问的访问范围代表。您应该使用device_authorization_endpoint元数据值从发现文档中检索此 URL。包括以下 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 ,您的设备应每五秒向 Google 的授权服务器发送一次轮询请求。有关详细信息,请参阅步骤 3
user_code一个区分大小写的值,用于向 Google 标识应用程序请求访问的范围。您的用户界面将指示用户在具有更丰富输入功能的单独设备上输入此值。然后,当提示用户授予对您的应用程序的访问权限时,Google 会使用该值显示正确的范围集。
verification_url用户必须在单独的设备上导航到的 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 字符集中的任何可打印字符。您向用户显示的内容应指示用户导航到单独设备上的user_code verification_url

设计您的用户界面 (UI) 时要牢记以下规则:

  • user_code
    • user_code必须显示在可以处理 15 个“W”大小字符的字段中。换句话说,如果您可以正确显示代码WWWWWWWWWWWWWWW ,那么您的 UI 是有效的,我们建议在测试user_code在您的 UI 中的显示方式时使用该字符串值。
    • user_code区分大小写,不应以任何方式修改,例如更改大小写或插入其他格式字符。
  • verification_url
    • 显示verification_url的空间必须足够宽以处理 40 个字符长的 URL 字符串。
    • 您不应该以任何方式修改verification_url ,除了可选地删除显示方案。如果您出于显示原因确实计划从 URL 中删除方案(例如https:// ),请确保您的应用程序可以同时处理httphttps变体。

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

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

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

要轮询的端点的 URL 是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 后,用户会看到如下所示的同意屏幕:

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

第 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"
}

其他错误

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

错误HTTP 状态码描述
invalid_client 401未找到 OAuth 客户端。例如,如果client_id参数值无效,则会发生此错误。
invalid_grant 400 code参数值无效。
unsupported_grant_type 400 grant_type参数值无效。

调用 Google API

在您的应用程序获得访问令牌后,如果已授予 API 所需的访问范围,您可以使用该令牌代表给定用户帐户调用 Google API。为此,请通过包含access_token查询参数或Authorization HTTP 标头Bearer值,在对 API 的请求中包含访问令牌。如果可能,最好使用 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_idAPI Console获取的客户端 ID。
client_secretAPI 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"
}

请注意,将发行的刷新令牌的数量是有限制的;每个客户端/用户组合一个限制,所有客户端的每个用户另一个限制。您应该将刷新令牌保存在长期存储中,并在它们保持有效时继续使用它们。如果您的应用程序请求太多刷新令牌,它可能会遇到这些限制,在这种情况下,较旧的刷新令牌将停止工作。

撤销令牌

在某些情况下,用户可能希望撤销对应用程序的访问权限。用户可以通过访问Account Settings撤销访问。有关更多信息,请参阅有权访问您的帐户支持文档的第三方网站和应用程序的删除网站或应用程序访问部分

应用程序也可以通过编程方式撤销授予它的访问权限。在用户取消订阅、删除应用程序或应用程序所需的 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 连接谷歌登录

  • email
  • openid
  • profile

驱动 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