适用于移动应用和桌面应用的 OAuth 2.0

。概览概述了 Google 支持的 OAuth 2.0 流程,可帮助您确保为应用选择合适的流程。

本文档介绍了在手机、平板电脑和计算机上安装的应用如何使用 Google 的 OAuth 2.0 端点向 Google API 授予访问权限。

OAuth 2.0 允许用户与应用共享特定数据,同时保持用户名、密码和其他信息的私密性。例如,应用可以使用 OAuth 2.0 获得用户许可,将文件存储在他们的 Google 云端硬盘中。

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

此授权流程类似于网络服务器应用所使用的授权流程。主要区别在于,已安装的应用必须打开系统浏览器并提供本地重定向 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,请使用搜索功能查找该 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 架构。

Android
  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 API 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。商店 ID 是每个 Apple App Store 网址中包含的一个数字字符串。

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

      示例:https://apps.apple.com/app/google/id284815942

  5. (可选)

    输入您的团队 ID。如需了解详情,请参阅 Apple 开发者帐号文档中的查找您的团队 ID

  6. 点击创建
超宽带
  1. 选择通用 Windows 平台应用类型。
  2. 输入 OAuth 客户端的名称。此名称会显示在项目的 Credentials page 中,用于标识客户端。
  3. 输入应用的 Microsoft 商店 ID(12 个字符)。您可以在 Microsoft 合作伙伴中心的“应用管理”部分内的应用身份页面上找到此值。
  4. 点击创建

对于 UWP 应用,自定义 URI 架构不得超过 39 个字符。

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

如需使用此网址接收授权代码,您的应用必须监听本地网络服务器。许多(但非全部)平台上都可以做到这一点。但是,如果您的平台支持,那么这是获取授权代码的推荐机制。

当您的应用收到授权响应时,为获得最佳易用性,它应通过显示一个 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 是高熵加密随机字符串,使用非保留字符 [A-Z] / [a-z] / [0-9] / "-" / "." / "_" / "~",最少 43 个字符,最大 128 个字符。

代码验证程序应具有足够的熵,使得猜测值变得不切实际。

创建代码挑战

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

代码质询生成方法
S256(推荐) 代码质询是代码验证程序的 Base64网址(无填充)编码 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 Console Credentials page中找到此值。

redirect_uri 必需

确定 Google 的授权服务器如何向您的应用发送响应。有多个安装选项可用于安装式应用,您在设置授权凭据时考虑到了特定的重定向方法。

该值必须与您在客户端的 API Console Credentials 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 网址不同)开头。
环回 IP 地址 http://127.0.0.1:porthttp://[::1]:port

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

请注意,移动应用支持的环回 IP 地址重定向选项 已弃用

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 推荐

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

您可以将此参数用于多种用途,例如将用户定向到应用中的正确资源、发送 Nonce 以及减少跨网站请求伪造。由于您的 redirect_uri 是可以猜测的,因此使用 state 值可提高您对传入连接是否是身份验证请求结果的保证。如果您生成随机字符串,或对捕获客户端状态的 Cookie 或其他值进行哈希处理,则可以验证响应,以进一步确保请求和响应来自同一浏览器,从而防范跨站请求伪造等攻击。如需查看有关如何创建和确认 state 令牌的示例,请参阅 OpenID Connect 文档。

login_hint 可选

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

将参数值设为电子邮件地址或 sub 标识符(等同于用户的 Google ID)。

授权网址示例

以下标签页显示了不同重定向 URI 选项的示例授权网址。

除了 redirect_uri 参数的值外,这些网址完全相同。这些网址还包含所需的 response_typeclient_id 参数以及可选的 state 参数。为便于阅读,每个网址都包含换行符和空格。

自定义 URI 架构

https://accounts.google.com/o/oauth2/v2/auth?
 scope=&
 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=&
 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

第 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 政策所禁止的嵌入式用户代理中。

Android

Android 开发者在 android.webkit.WebView 中打开授权请求时可能会遇到此错误消息。开发者应改用 Android 库,如 Google 登录 Android 版或 OpenID Foundation 的 AppAuth for Android

当 Android 应用在嵌入式用户代理中打开常规 Web 链接,并且用户从您的网站转到 Google 的 OAuth 2.0 授权端点时,Web 开发者可能会遇到此错误。开发者应允许操作系统的默认链接处理程序(包括 Android App Links 处理程序或默认浏览器应用)中打开常规链接。Android 自定义标签页库也受支持。

iOS

iOS 和 macOS 开发者在 WKWebView 中打开授权请求时可能会遇到此错误。开发者应改用 iOS 库,例如 iOS 版 Google 登录或 OpenID Foundation 的 AppAuth for iOS

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

org_internal

请求中的 OAuth 客户端 ID 属于某个项目,该项目限制了对特定 Google Cloud 组织中的 Google 帐号的访问。如需详细了解此配置选项,请参阅“设置 OAuth 权限请求页面”帮助文章中的用户类型部分。

invalid_grant

如果您使用了代码验证程序和验证,则 code_callenge 参数无效或缺失。确保已正确设置 code_challenge 参数。

刷新访问令牌时,此令牌可能已过期或已失效。 再次验证用户身份,并请求用户同意以获取新令牌。如果仍看到此错误,请确保您的应用已正确配置,并且您在请求中使用了正确的令牌和参数。否则,用户帐号可能已被删除或停用。

redirect_uri_mismatch

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

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

redirect_uri 参数可能是指已废弃且不再受支持的 OAuth 带外 (OOB) 流程。请参阅迁移指南以更新您的集成。

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

应用收到授权响应的方式取决于其使用的重定向 URI 架构。无论采用哪种方案,响应都会包含授权代码 (code) 或错误 (error)。例如,error=access_denied 表示用户已拒绝请求。

如果用户向您的应用授予访问权限,您可以使用授权代码交换访问令牌和刷新令牌,如下一步中所述。

第 5 步:使用授权代码交换刷新令牌和访问令牌

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

字段
client_id 从 API Console Credentials page获取的客户端 ID。
client_secret 从 API Console Credentials page获取的客户端密钥。
code 初始请求返回的授权代码。
code_verifier 您在第 1 步中创建的代码验证程序。
grant_type 如 OAuth 2.0 规范中所定义,此字段的值必须设置为 authorization_code
redirect_uri 在 API Console Credentials page 中,为给定 client_id 为您的项目列出的其中一个重定向 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=http://127.0.0.1:9004&
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

在您的应用获得访问令牌后,您可以使用该令牌代表指定用户帐号调用 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获取的客户端密钥。(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"
}

请注意,将颁发的刷新令牌数量有限;每个客户端/用户组合一个限制,所有客户端上的每个用户都不同。您应将刷新令牌保存在长期存储空间中,只要令牌仍然有效,就可以继续使用。如果您的应用请求刷新令牌的次数过多,则可能会遇到这些限制,在这种情况下,较旧的刷新令牌将停止工作。

撤消令牌

在某些情况下,用户可能希望撤消对应用的访问权限。用户可以通过访问 帐号设置来撤消访问权限。如需了解详情,请参阅有权访问您帐号的第三方网站和应用的“撤消网站或应用访问权限”部分

应用也能够以编程方式撤消授予它的访问权限。用户退订、移除应用,或者应用所需的 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 当前最佳做法适用于原生应用的 OAuth 2.0 制定了此处所述的许多最佳做法。