用于移动和桌面应用程序的 OAuth 2.0

本文档解释了安装在手机、平板电脑和计算机等设备上的应用程序如何使用 Google 的 OAuth 2.0 端点来授权访问 Google API。

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

已安装的应用程序分发到各个设备,并且假定这些应用程序无法保密。当用户在应用程序中或应用程序在后台运行时,他们可以访问 Google API。

此授权流程类似于用于Web 服务器应用程序的授权流程。主要区别在于已安装的应用程序必须打开系统浏览器并提供本地重定向 URI 来处理来自 Google 授权服务器的响应。

备择方案

对于移动应用程序,您可能更喜欢使用适用于AndroidiOS的 Google 登录。 Google 登录客户端库处理身份验证和用户授权,它们可能比此处描述的较低级别协议更易于实现。

对于在不支持系统浏览器或输入功能有限的设备(例如电视、游戏机、相机或打印机)上运行的应用程序,请参阅适用于电视和设备的 OAuth 2.0在电视和有限输入设备上登录

库和样本

我们推荐以下库和示例来帮助您实施本文档中描述的 OAuth 2.0 流程:

先决条件

为您的项目启用 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. 以下部分描述了 Google 授权服务器支持的客户端类型和重定向方法。选择为您的应用程序推荐的客户端类型,命名您的 OAuth 客户端,并根据需要设置表单中的其他字段。

自定义 URI 方案(Android、iOS、UWP)

建议为 Android 应用、iOS 应用和通用 Windows 平台 (UWP) 应用使用自定义 URI 方案。

安卓
  1. 选择Android应用程序类型。
  2. 输入 OAuth 客户端的名称。此名称显示在项目的 Credentials page 上以识别客户端。
  3. 输入您的 Android 应用程序的包名称。此值在应用清单文件<manifest>元素的package属性中定义。
  4. 输入应用分发的 SHA-1 签名证书指纹。
    • 如果您的应用使用Google Play应用签名,请从 Play 管理中心的应用签名页面复制 SHA-1 指纹。
    • 如果您管理自己的密钥库和签名密钥,请使用 Java 随附的keytool实用程序以人类可读的格式打印证书信息。复制keytool输出的Certificate fingerprints部分中的SHA1值。有关更多信息,请参阅 Google APIs for Android 文档中的验证您的客户端
  5. 单击创建
iOS
  1. 选择iOS应用程序类型。
  2. 输入 OAuth 客户端的名称。此名称显示在项目的 Credentials page 上以识别客户端。
  3. 输入您的应用程序的捆绑标识符。捆绑包 ID 是应用程序的信息属性列表资源文件 ( info.plist ) 中CFBundleIdentifier键的值。该值最常显示在 Xcode 项目编辑器的 General 窗格或 Signing & Capabilities 窗格中。捆绑包 ID 也显示在Apple 的 App Store Connect 网站上的应用程序信息页面的一般信息部分。
  4. (选修的)

    如果应用程序在 Apple 的 App Store 中发布,请输入您的应用程序的 App Store ID。 Store ID 是包含在每个 Apple App Store URL 中的数字字符串。

    1. 在您的 iOS 或 iPadOS 设备上打开Apple App Store 应用程序
    2. 搜索您的应用。
    3. 选择共享按钮(方形和向上箭头符号)。
    4. 选择复制链接
    5. 将链接粘贴到文本编辑器中。 App Store ID 是 URL 的最后一部分。

      示例: https://apps.apple.com/app/google/id 284815942

  5. (选修的)

    输入您的团队 ID。有关更多信息,请参阅在 Apple 开发者帐户文档中查找您的团队 ID

  6. 单击创建
UWP
  1. 选择通用 Windows 平台应用程序类型。
  2. 输入 OAuth 客户端的名称。此名称显示在项目的 Credentials page 上以识别客户端。
  3. 输入应用的 12 个字符的 Microsoft Store ID。您可以在Microsoft 合作伙伴中心应用程序标识页面的应用程序管理部分中找到此值。
  4. 单击创建

对于 UWP 应用,自定义 URI 方案不能超过 39 个字符。

环回 IP 地址(macOS、Linux、Windows 桌面)

要使用此 URL 接收授权代码,您的应用程序必须在本地 Web 服务器上进行侦听。这在许多但不是所有平台上都是可能的。但是,如果您的平台支持,这是获取授权码的推荐机制。

当您的应用收到授权响应时,为了获得最佳可用性,它应该通过显示指示用户关闭浏览器并返回您的应用的 HTML 页面来响应。

推荐使用macOS、Linux 和 Windows 桌面(但不是通用 Windows 平台)应用程序
表单值将应用程序类型设置为Desktop app

手动复制/粘贴

确定访问范围

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

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

OAuth 2.0 API 范围文档包含您可能用于访问 Google API 的范围的完整列表。

获取 OAuth 2.0 访问令牌

以下步骤展示了您的应用程序如何与 Google 的 OAuth 2.0 服务器交互以获得用户的同意以代表用户执行 API 请求。您的应用程序必须先获得该同意,然后才能执行需要用户授权的 Google API 请求。

第 1 步:生成代码验证器和质询

Google 支持代码交换证明密钥(PKCE) 协议,以使安装的应用程序流程更加安全。为每个授权请求创建一个唯一的验证码,并将其转换后的值,称为“code_challenge”,发送到授权服务器以获取授权码。

创建代码验证器

code_verifier是使用非保留字符 [AZ] / [az] / [0-9] / "-" / "." 的高熵密码随机字符串。 /“_”/“~”,最小长度为 43 个字符,最大长度为 128 个字符。

代码验证器应该有足够的熵来使猜测值变得不切实际。

创建代码挑战

支持两种创建代码质询的方法。

代码挑战生成方法
S256(推荐)代码质询是代码验证器的 Base64URL(无填充)编码的 SHA256 哈希。
code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))
清楚的代码质询与上面生成的代码验证器的值相同。
code_challenge = code_verifier

第 2 步:向 Google 的 OAuth 2.0 服务器发送请求

要获得用户授权,请通过https://accounts.google.com/o/oauth2/v2/auth向 Google 的授权服务器发送请求。此端点处理活动会话查找、对用户进行身份验证并获得用户同意。端点只能通过 SSL 访问,它拒绝 HTTP(非 SSL)连接。

授权服务器支持已安装应用程序的以下查询字符串参数:

参数
client_id必需的

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

redirect_uri必需的

确定 Google 的授权服务器如何向您的应用发送响应。已安装的应用程序可以使用多个重定向选项,并且您将在设置授权凭据时考虑到特定的重定向方法。

该值必须与您在客户端的 API ConsoleCredentials page中配置的 OAuth 2.0 客户端的授权重定向 URI 之一完全匹配。如果此值与授权的 URI 不匹配,您将收到redirect_uri_mismatch错误。

下表显示了每种方法的相应redirect_uri参数值:

redirect_uri
自定义 URI 方案com.example.app : redirect_uri_path

或者

com.googleusercontent.apps.123 : redirect_uri_path
  • com.example.app是您控制的域的反向 DNS 表示法。自定义方案必须包含一个有效的句点。
  • com.googleusercontent.apps.123是客户端 ID 的反向 DNS 表示法。
  • redirect_uri_path是一个可选的路径组件,例如/oauth2redirect 。请注意,路径应以单斜杠开头,这与常规 HTTP URL 不同。
环回 IP 地址http://127.0.0.1: porthttp://[::1]: port

查询您的平台以获取相关的环回 IP 地址,并在随机可用端口上启动 HTTP 侦听器。用您的应用程序侦听的实际端口号替换port

手动复制/粘贴urn:ietf:wg:oauth:2.0:oob
程序化提取urn:ietf:wg:oauth:2.0:oob:auto
response_type必需的

确定 Google OAuth 2.0 端点是否返回授权代码。

将参数值设置为已安装应用程序的code

scope必需的

以空格分隔的范围列表,用于标识您的应用程序可以代表用户访问的资源。这些值通知 Google 向用户显示的同意屏幕。

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

code_challenge受到推崇的

指定在授权代码交换期间将用作服务器端质询的编码code_verifier 。有关更多信息,请参阅上面的创建代码挑战部分。

code_challenge_method受到推崇的

指定使用什么方法来编码将在授权代码交换期间使用的code_verifier 。此参数必须与上述code_challenge参数一起使用。如果包含code_challenge的请求中不存在,则code_challenge_method的值默认为plain 。此参数唯一支持的值是S256plain

state受到推崇的

指定应用程序用来维护授权请求和授权服务器响应之间的状态的任何字符串值。在用户同意或拒绝您的应用程序的访问请求后,服务器会返回您作为name=value对在redirect_uri的 URL 片段标识符 ( # ) 中发送的确切值。

您可以将此参数用于多种目的,例如将用户引导至应用程序中的正确资源、发送 nonce 和减少跨站点请求伪造。由于可以猜到您的redirect_uri ,因此使用state值可以增加您对传入连接是身份验证请求结果的保证。如果您生成随机字符串或对 cookie 的哈希值或捕获客户端状态的其他值进行编码,则可以验证响应以另外确保请求和响应源自同一浏览器,从而防止跨站点等攻击请求伪造。有关如何创建和确认state令牌的示例,请参阅OpenID Connect文档。

login_hint选修的

如果您的应用程序知道哪个用户正在尝试进行身份验证,它可以使用此参数向 Google 身份验证服务器提供提示。服务器使用提示来简化登录流程,方法是预先填写登录表单中的电子邮件字段或选择适当的多登录会话。

将参数值设置为电子邮件地址或sub标识符,相当于用户的 Google ID。

示例授权 URL

下面的选项卡显示了不同重定向 URI 选项的示例授权 URL。

除了redirect_uri参数的值之外,这些 URL 是相同的。 URL 还包含必需的response_typeclient_id参数以及可选的state参数。每个 URL 都包含换行符和空格以提高可读性。

自定义 URI 方案

https://accounts.google.com/o/oauth2/v2/auth?
 scope=email%20profile&
 response_type=code&
 state=security_token%3D138r5719ru3e1%26url%3Dhttps%3A%2F%2Foauth2.example.com%2Ftoken&
 redirect_uri=com.example.app%3A/oauth2redirect&
 client_id=client_id

环回 IP 地址

https://accounts.google.com/o/oauth2/v2/auth?
 scope=email%20profile&
 response_type=code&
 state=security_token%3D138r5719ru3e1%26url%3Dhttps%3A%2F%2Foauth2.example.com%2Ftoken&
 redirect_uri=http%3A//127.0.0.1%3A9004&
 client_id=client_id

复制粘贴

https://accounts.google.com/o/oauth2/v2/auth?
 scope=email%20profile&
 response_type=code&
 state=security_token%3D138r5719ru3e1%26url%3Dhttps%3A%2F%2Foauth2.example.com%2Ftoken&
 redirect_uri=urn%3Aietf%3Awg%3Aoauth%3A2.0%3Aoob&
 client_id=client_id

程序化提取

https://accounts.google.com/o/oauth2/v2/auth?
 scope=email%20profile&
 response_type=code&
 state=security_token%3D138r5719ru3e1%26url%3Dhttps%3A%2F%2Foauth2.example.com%2Ftoken&
 redirect_uri=urn%3Aietf%3Awg%3Aoauth%3A2.0%3Aoob%3Aauto&
 client_id=client_id

第 3 步:Google 提示用户同意

在此步骤中,用户决定是否授予您的应用程序请求的访问权限。在此阶段,Google 会显示一个同意窗口,其中显示您的应用程序的名称以及它正在使用用户的授权凭据请求访问权限的 Google API 服务以及要授予的访问范围的摘要。然后,用户可以同意授予对您的应用程序请求的一个或多个范围的访问权限或拒绝该请求。

您的应用程序在此阶段不需要执行任何操作,因为它等待来自 Google 的 OAuth 2.0 服务器的响应,指示是否授予任何访问权限。该响应将在以下步骤中解释。

错误

对 Google 的 OAuth 2.0 授权端点的请求可能会显示面向用户的错误消息,而不是预期的身份验证和授权流程。下面列出了常见的错误代码和建议的解决方法。

admin_policy_enforced

由于 Google Workspace 管理员的政策,Google 帐户无法授权请求的一个或多个范围。请参阅 Google Workspace 管理员帮助文章控制哪些第三方和内部应用访问 Google Workspace 数据,详细了解管理员如何限制对所有范围或敏感和受限范围的访问,直到明确授予您的 OAuth 客户端 ID 访问权限。

disallowed_useragent

授权端点显示在 Google 的OAuth 2.0 Policies不允许的嵌入式用户代理中。

安卓

Android 开发者在android.webkit.WebView中打开授权请求时可能会遇到此错误消息。开发人员应改为使用 Android 库,例如适用于 Android 的Google Sign-In 或适用于 Android的 OpenID Foundation 的AppAuth

当 Android 应用程序在嵌入式用户代理中打开常规 Web 链接并且用户从您的站点导航到 Google 的 OAuth 2.0 授权端点时,Web 开发人员可能会遇到此错误。开发人员应允许在操作系统的默认链接处理程序中打开通用链接,其中包括Android 应用程序链接处理程序或默认浏览器应用程序。 Android 自定义选项卡库也是受支持的选项。

iOS

iOS 和 macOS 开发者在WKWebView打开授权请求时可能会遇到这个错误。开发人员应改为使用 iOS 库,例如适用于 iOS 的Google Sign-In 或适用于 iOS的 OpenID Foundation 的AppAuth

当 iOS 或 macOS 应用程序在嵌入式用户代理中打开常规 Web 链接并且用户从您的站点导航到 Google 的 OAuth 2.0 授权端点时,Web 开发人员可能会遇到此错误。开发人员应允许在操作系统的默认链接处理程序中打开通用链接,其中包括通用链接处理程序或默认浏览器应用程序。SFSafariViewController库也是受支持的选项。

org_internal

请求中的 OAuth 客户端 ID 是限制对特定Google Cloud Organization中的 Google Accounts 访问的项目的一部分。有关此配置选项的更多信息,请参阅设置 OAuth 同意屏幕帮助文章中的用户类型部分。

redirect_uri_mismatch

授权请求中传递的redirect_uri与 OAuth 客户端 ID 的授权重定向 URI 不匹配。查看 Google API Console Credentials page中的授权重定向 URI。

传递的redirect_uri对于客户端类型可能无效。

第 4 步:处理 OAuth 2.0 服务器响应

您的应用程序接收授权响应的方式取决于它使用的重定向 URI 方案。无论采用何种方案,响应都将包含授权代码 ( code ) 或错误 ( error )。例如, error=access_denied表示用户拒绝了请求。

如果用户授予对您的应用程序的访问权限,您可以将授权代码交换为访问令牌和刷新令牌,如下一步所述。

第 5 步:交换授权码以获取刷新和访问令牌

要交换访问令牌的授权代码,请调用https://oauth2.googleapis.com/token端点并设置以下参数:

字段
client_id从 API ConsoleCredentials page获取的客户端 ID。
client_secret从 API ConsoleCredentials page获得的客户端密码。
code从初始请求返回的授权码。
code_verifier您在步骤 1中创建的代码验证器。
grant_type正如 OAuth 2.0 规范中定义的那样,该字段的值必须设置为authorization_code
redirect_uri在给定client_id的 API ConsoleCredentials page 中为您的项目列出的重定向 URI 之一。

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

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

code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7&
client_id=your_client_id&
client_secret=your_client_secret&
redirect_uri=urn%3Aietf%3Awg%3Aoauth%3A2.0%3Aoob%3Aauto&
grant_type=authorization_code

Google 通过返回一个包含短期访问令牌和刷新令牌的 JSON 对象来响应此请求。

响应包含以下字段:

字段
access_token您的应用程序为授权 Google API 请求而发送的令牌。
expires_in访问令牌的剩余生命周期(以秒为单位)。
id_token注意:仅当您的请求包含身份范围(例如openidprofileemail )时,才会返回此属性。该值是一个 JSON Web 令牌 (JWT),其中包含有关用户的数字签名身份信息。
refresh_token可用于获取新访问令牌的令牌。在用户撤销访问之前,刷新令牌一直有效。请注意,始终为已安装的应用程序返回刷新令牌。
scope access_token授予的访问范围,以空格分隔、区分大小写的字符串列表表示。
token_type返回的令牌类型。此时,该字段的值始终设置为Bearer

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

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

调用 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获得的客户端密码。 ( client_secret不适用于注册为 Android、iOS 或 Chrome 应用程序的客户端的请求。)
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以及错误代码。

延伸阅读

IETF Best Current Practice OAuth 2.0 for Native Apps建立了此处记录的许多最佳实践。