为服务器到服务器应用使用 OAuth 2.0

Google OAuth 2.0 系统支持服务器与服务器之间的互动,例如网络间的互动。 和 Google 服务。对于这种情况,您需要一个服务账号, 是属于您的应用程序而不是某个最终用户的账号。您的 应用会代表服务账号调用 Google API,这样用户就不必直接 。这种情况有时称为“两方模式的 OAuth”,或“2LO”。(相关字词 “三足式 OAuth”是指您的应用代表调用 Google API 的场景 最终用户的内容,有时需要征得用户同意。)

通常,当应用使用 Google API 工作时,会使用服务账号 自己的数据,而不是用户的数据。例如,使用 Google Cloud 的 用于数据持久化的数据存储区将使用服务账号来验证其对 Google Cloud Datastore API。

Google Workspace 网域管理员还可以 向服务账号授予全网域权限,以访问用户 代表域内用户共享的数据。

本文档介绍应用如何通过以下方式完成服务器到服务器 OAuth 2.0 流程: 使用 Google API 客户端库(推荐)或 HTTP。

概览

要支持服务器与服务器之间的交互,请先在以下位置为您的项目创建一个服务账号: API Console。如果您想访问以下国家/地区用户的用户数据: 您的 Google Workspace 账号,然后向该服务账号授予全网域访问权限。

然后,您的应用会准备使用服务账号的 向 OAuth 2.0 身份验证服务器请求访问令牌。

最后,您的应用可以使用访问令牌来调用 Google API。

创建服务账号

服务账号的凭据包含一个生成的电子邮件地址,该地址是唯一的,并且至少 一个公钥/私钥对。如果启用了全网域授权功能,那么客户端 ID 也会 服务账号的凭据

如果您的应用在 Google App Engine 上运行,系统将在 创建项目时所需的资源。

如果您的应用在 Google Compute Engine 上运行,则系统还会设置一个服务账号 但在创建项目时自动创建,但必须指定 应用需要的访问权限。有关 请参阅 准备实例以使用服务账号

如果您的应用未在 Google App Engine 或 Google Compute Engine 上运行,您必须获取 Google API Console中的这些凭据。生成服务账号 凭据,或者要查看已生成的公共凭据,请执行以下操作:

首先,创建一个服务帐户:

  1. 打开 Service accounts page
  2. If prompted, select a project, or create a new one.
  3. 单击创建服务帐户
  4. Service account details下,键入服务帐户的名称、ID 和描述,然后点击Create and continue
  5. 可选:在Grant this service account access to project下,选择要授予服务帐户的 IAM 角色。
  6. 单击继续
  7. 可选:在Grant users access to this service account下,添加允许使用和管理服务帐户的用户或组。
  8. 单击完成

接下来,创建一个服务帐户密钥:

  1. 单击您创建的服务帐户的电子邮件地址。
  2. 单击密钥选项卡。
  3. 添加密钥下拉列表中,选择创建新密钥
  4. 单击创建

您的新公钥/私钥对已生成并下载到您的机器上;它作为私钥的唯一副本。您有责任安全地存储它。如果您丢失了这个密钥对,您将需要生成一个新的。

您可以返回到 API Console 随时查看该电子邮件地址,公开 密钥指纹和其他信息,或生成额外的公钥/私钥对。对于 请参阅 API Console,参见 API Console中的服务账号 帮助文件。

记下服务账号的电子邮件地址并存储服务账号的私钥 文件放在您的应用可访问的位置。您的应用需要它们 授权的 API 调用。

将全网域授权委派给服务账号

组织的 Workspace 管理员可以使用 Google Workspace 账号向 应用来代表 Google Workspace 网域中的用户访问 Workspace 用户数据。例如: 使用 Google 日历 API 将活动添加到 Google Workspace 网域将使用服务账号访问 。授权服务账号代表网域中的用户访问数据 有时也称为“全网域授权”服务账号

要将全网域授权委派给某个服务账号, Workspace 网域必须完成以下步骤:

  1. 来自您的 Google Workspace 网域的 管理控制台中,前往主菜单 安全 > 访问权限和数据控制 >API 控件
  2. 全网域授权窗格中,选择管理全网域授权
  3. 点击新增
  4. 客户端 ID 字段中,输入服务账号的客户端 ID。您可以 您的服务账号的客户端 ID Service accounts page
  5. OAuth 范围(以英文逗号分隔)字段中,输入您的授权范围列表 应向其授予访问权限。例如,如果您的应用需要 拥有 Google Drive API 和 Google Calendar API 的完整访问权限,请输入: https://www.googleapis.com/auth/drive, https://www.googleapis.com/auth/calendar
  6. 点击授权

您的应用现在有权以 Workspace 网域中的用户的身份进行 API 调用( "模拟"用户)。在准备进行这些委托 API 调用时,您需要明确指定用户 模拟。

准备进行委托 API 调用

Java

在您从 API Console,请使用 适用于 Java 的 Google API 客户端库 利用服务账号的凭据创建 GoogleCredential 对象 您的应用需要访问的范围。例如:

import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.services.sqladmin.SQLAdminScopes;

// ...

GoogleCredential credential = GoogleCredential.fromStream(new FileInputStream("MyProject-1234.json"))
    .createScoped(Collections.singleton(SQLAdminScopes.SQLSERVICE_ADMIN));

如果您在 Google Cloud Platform 上开发应用,可以使用 应用默认凭据 这样可以简化流程

委托全网域授权

如果您已向服务账号授予了全网域访问权限,并且想要模拟 用户账号,请使用 GoogleCredential 对象的 createDelegated 方法。例如:

GoogleCredential credential = GoogleCredential.fromStream(new FileInputStream("MyProject-1234.json"))
    .createScoped(Collections.singleton(SQLAdminScopes.SQLSERVICE_ADMIN))
    .createDelegated("workspace-user@example.com");

上面的代码使用 GoogleCredential 对象来调用其 createDelegated() 方法。createDelegated() 方法的参数必须是属于 Workspace 账号。发出请求的代码将使用此凭据来调用 Google API 访问 Cloud Storage 存储分区。

Python

在您从 API Console,请使用 适用于 Python 的 Google API 客户端库 以完成下列步骤:

  1. 根据服务账号的凭据和Credentials 范围。例如:
    from google.oauth2 import service_account
    
    SCOPES = ['https://www.googleapis.com/auth/sqlservice.admin']
    SERVICE_ACCOUNT_FILE = '/path/to/service.json'
    
    credentials = service_account.Credentials.from_service_account_file(
            SERVICE_ACCOUNT_FILE, scopes=SCOPES)

    如果您在 Google Cloud Platform 上开发应用,可以使用 应用默认凭据 这样可以简化流程

  2. 委托全网域授权

    如果您已向服务账号授予了全网域访问权限,并且希望 模拟用户账号,请使用现有用户账号的 with_subject 方法 ServiceAccountCredentials 对象。例如:

    delegated_credentials = credentials.with_subject('user@example.org')

使用 Credentials 对象调用您应用中的 Google API。

HTTP/REST

在您从 API Console,您的应用需要完成 操作步骤:

  1. 创建一个 JSON Web 令牌(JWT,发音为“jot”),其中包括标头、声明集、 和签名。
  2. 从 Google OAuth 2.0 授权服务器请求访问令牌。
  3. 处理授权服务器返回的 JSON 响应。

以下部分介绍了如何完成这些步骤。

如果响应包含访问令牌,您可以使用访问令牌 调用 Google API。(如果响应不包含访问权限 令牌,则您的 JWT 和令牌请求格式不正确,或者服务账号可能 无权访问所请求的范围。)

当访问令牌到期时,您的应用将生成另一个 JWT、为其签名并请求另一个访问令牌。

您的服务器应用使用 JWT 向 Google
                  授权服务器,然后使用该令牌调用 Google API 端点。否
                  最终用户的参与。

本部分的其余内容将具体介绍如何创建 JWT、为 JWT 签名, 组成访问令牌请求并处理响应。

创建 JWT

JWT 由三部分组成:标头、声明集和 签名。标头和声明集是 JSON 对象。系统会将这些 JSON 对象序列化 UTF-8 字节,然后使用 Base64url 编码进行编码。这种编码能够提供 避免因重复编码操作而导致的编码变化。标头、声明集和 签名通过句点 (.) 字符串联在一起。

JWT 的组成如下:

{Base64url encoded header}.{Base64url encoded claim set}.{Base64url encoded signature}

签名的基本字符串如下所示:

{Base64url encoded header}.{Base64url encoded claim set}
构建 JWT 标头

标头包含三个字段,用于表示签名算法、 该断言和 [服务账号的密钥 ID] key](https://cloud.google.com/iam/docs/reference/rest/v1/projects.serviceAccounts.keys) 签名 JWT。算法和格式是必填项,每个字段只有 一个值。此标题会随着更多算法和格式的引入而发生变化 。密钥 ID 是可选的,如果指定的密钥 ID 不正确,GCP 将尝试 与服务账号关联的所有密钥,以验证令牌并拒绝令牌(如果 未找到有效密钥。Google 保留拒绝密钥 ID 不正确的令牌的权利 。

服务账号依赖于 RSA SHA-256 算法和 JWT 令牌格式。因此 标头的 JSON 表示法如下所示:

{"alg":"RS256","typ":"JWT", "kid":"370ab79b4513eb9bad7c9bd16a95cb76b5b2a56a"}

其 Base64url 表示形式如下所示:

          eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsICJraWQiOiIzNzBhYjc5YjQ1MTNlYjliYWQ3YzliZDE2YTk1Y2I3NmI1YjJhNTZhIn0=
构建 JWT 声明集

JWT 声明集包含 JWT 的相关信息,包括 请求(范围)、令牌的目标、颁发者、令牌颁发的时间 以及令牌的生命周期大部分字段是必填字段。与 JWT 标头一样, JWT 声明集是一个 JSON 对象,用于计算签名。

必需的声明

JWT 声明集中的必需声明如下所示。它们可能会以任意顺序展示 声明集。

名称 说明
iss 服务账号的电子邮件地址。
scope 应用请求的权限列表(用空格分隔)。
aud 断言预期目标的描述符。创建访问令牌时 请求此值始终为 https://oauth2.googleapis.com/token
exp 断言的到期时间,以自世界协调时间 (UTC) 00:00:00 以来的秒数表示。 1970 年 1 月 1 日。此值最多比发放时间晚 1 小时。
iat 发出断言的时间,以自世界协调时间 (UTC) 00:00:00 以来的秒数表示。 1970 年 1 月 1 日。

JWT 声明集中必填字段的 JSON 表示法如下所示:

{
  "iss": "761326798069-r5mljlln1rd4lrbhg75efgigp36m78j5@developer.gserviceaccount.com",
  "scope": "https://www.googleapis.com/auth/devstorage.read_only",
  "aud": "https://oauth2.googleapis.com/token",
  "exp": 1328554385,
  "iat": 1328550785
}
其他版权主张

在某些企业情况下,应用可以使用全网域授权功能来代表操作 组织中特定用户的身份。执行此类假冒行为的权限 必须先获得权限,然后应用才能模拟用户,通常由 超级用户。如需了解详情,请参阅 使用全网域授权功能控制 API 访问权限

要获取访问令牌以向委托应用授予对某项资源的访问权限, 在 JWT 声明中包含用户的电子邮件地址,并将其设置为 sub 字段。

名称 说明
sub 应用请求委托的用户的电子邮件地址 访问权限。

如果应用程序无权模拟用户,则对 包含 sub 字段的访问令牌请求将是 错误

显示包含 sub 字段的 JWT 声明集示例 如下:

{
  "iss": "761326798069-r5mljlln1rd4lrbhg75efgigp36m78j5@developer.gserviceaccount.com",
  "sub": "some.user@example.com",
  "scope": "https://www.googleapis.com/auth/prediction",
  "aud": "https://oauth2.googleapis.com/token",
  "exp": 1328554385,
  "iat": 1328550785
}
对 JWT 声明集进行编码

与 JWT 标头一样,JWT 声明集应序列化为 UTF-8 和 Base64url-safe 编码。以下是 JWT 声明集的 JSON 表示法示例:

{
  "iss": "761326798069-r5mljlln1rd4lrbhg75efgigp36m78j5@developer.gserviceaccount.com",
  "scope": "https://www.googleapis.com/auth/prediction",
  "aud": "https://oauth2.googleapis.com/token",
  "exp": 1328554385,
  "iat": 1328550785
}
计算签名

JSON 网络签名 (JWS) 是一套规范,用于指导 JWT。签名的输入是以下内容的字节数组:

{Base64url encoded header}.{Base64url encoded claim set}

计算签名时,必须使用 JWT 标头中的签名算法。通过 只有 Google OAuth 2.0 授权服务器支持的签名算法是 SHA-256 哈希算法。它在 alg 中以 RS256 表示, 字段。

使用 SHA256withRSA(也称为 具有 SHA-256 哈希函数的 RSASSA-PKCS1-V1_5-SIGN)使用从 Google API Console。输出将是一个字节数组。

然后,必须对签名进行 Base64url 编码。标头、声明集和签名 使用句点 (.) 字符串联在一起。最终生成 JWT。它 应如下所示(为清楚起见,添加了换行符):

{Base64url encoded header}.
{Base64url encoded claim set}.
{Base64url encoded signature}

下面是未进行 Base64url 编码的 JWT 示例:

{"alg":"RS256","typ":"JWT"}.
{
"iss":"761326798069-r5mljlln1rd4lrbhg75efgigp36m78j5@developer.gserviceaccount.com",
"scope":"https://www.googleapis.com/auth/prediction",
"aud":"https://oauth2.googleapis.com/token",
"exp":1328554385,
"iat":1328550785
}.
[signature bytes]

以下是已签名并准备好传输的 JWT 示例:

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI3NjEzMjY3OTgwNjktcjVtbGpsbG4xcmQ0bHJiaGc3NWVmZ2lncDM2bTc4ajVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLCJzY29wZSI6Imh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2F1dGgvcHJlZGljdGlvbiIsImF1ZCI6Imh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL29hdXRoMi92NC90b2tlbiIsImV4cCI6MTMyODU1NDM4NSwiaWF0IjoxMzI4NTUwNzg1fQ.UFUt59SUM2_AW4cRU8Y0BYVQsNTo4n7AFsNrqOpYiICDu37vVt-tw38UKzjmUKtcRsLLjrR3gFW3dNDMx_pL9DVjgVHDdYirtrCekUHOYoa1CMR66nxep5q5cBQ4y4u2kIgSvChCTc9pmLLNoIem-ruCecAJYgI9Ks7pTnW1gkOKs0x3YpiLpzplVHAkkHztaXiJdtpBcY1OXyo6jTQCa3Lk2Q3va1dPkh_d--GU2M5flgd8xNBPYw4vxyt0mP59XZlHMpztZt0soSgObf7G3GXArreF_6tpbFsS3z2t5zkEiHuWJXpzcYr5zWTRPDEHsejeBSG8EgpLDce2380ROQ

发出访问令牌请求

在生成带签名的 JWT 后,应用可使用它来请求访问令牌。 此访问令牌请求是一个 HTTPS POST 请求,正文为 网址 编码。网址如下所示:

https://oauth2.googleapis.com/token

HTTPS POST 请求中需要以下参数:

名称 说明
grant_type 使用以下字符串,并根据需要进行网址编码: urn:ietf:params:oauth:grant-type:jwt-bearer
assertion JWT,包括签名。

以下是访问令牌中使用的 HTTPS POST 请求的原始转储 请求:

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

grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI3NjEzMjY3OTgwNjktcjVtbGpsbG4xcmQ0bHJiaGc3NWVmZ2lncDM2bTc4ajVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLCJzY29wZSI6Imh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2F1dGgvcHJlZGljdGlvbiIsImF1ZCI6Imh0dHBzOi8vYWNjb3VudHMuZ29vZ2xlLmNvbS9vL29hdXRoMi90b2tlbiIsImV4cCI6MTMyODU3MzM4MSwiaWF0IjoxMzI4NTY5NzgxfQ.ixOUGehweEVX_UKXv5BbbwVEdcz6AYS-6uQV6fGorGKrHf3LIJnyREw9evE-gs2bmMaQI5_UbabvI4k-mQE4kBqtmSpTzxYBL1TCd7Kv5nTZoUC1CmwmWCFqT9RE6D7XSgPUh_jF1qskLa2w0rxMSjwruNKbysgRNctZPln7cqQ

以下是同一个请求,使用 curl

curl -d 'grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI3NjEzMjY3OTgwNjktcjVtbGpsbG4xcmQ0bHJiaGc3NWVmZ2lncDM2bTc4ajVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLCJzY29wZSI6Imh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2F1dGgvcHJlZGljdGlvbiIsImF1ZCI6Imh0dHBzOi8vYWNjb3VudHMuZ29vZ2xlLmNvbS9vL29hdXRoMi90b2tlbiIsImV4cCI6MTMyODU3MzM4MSwiaWF0IjoxMzI4NTY5NzgxfQ.RZVpzWygMLuL-n3GwjW1_yhQhrqDacyvaXkuf8HcJl8EtXYjGjMaW5oiM5cgAaIorrqgYlp4DPF_GuncFqg9uDZrx7pMmCZ_yHfxhSCXru3gbXrZvAIicNQZMFxrEEn4REVuq7DjkTMyCMGCY1dpMa8aWfTQFt3Eh7smLchaZsU
' https://oauth2.googleapis.com/token

处理响应

如果 JWT 和访问令牌请求的格式正确,且服务账号 执行该操作的权限,以及来自授权服务器的 JSON 响应 包含访问令牌以下是一个示例响应:

{
  "access_token": "1/8xbJqaOZXSUZbHLl5EOtu1pxz3fmmetKx9W8CV4t79M",
  "scope": "https://www.googleapis.com/auth/prediction"
  "token_type": "Bearer",
  "expires_in": 3600
}

访问令牌可在指定的持续时间内重复使用 expires_in 值。

调用 Google API

Java

使用 GoogleCredential 对象调用 Google API,方法是完成 操作步骤:

  1. 使用 GoogleCredential 对象。例如:
    SQLAdmin sqladmin =
        new SQLAdmin.Builder(httpTransport, JSON_FACTORY, credential).build();
  2. 使用 该服务对象提供的接口。 例如,要列出精彩示例-123 中的 Cloud SQL 数据库实例, 项目:
    SQLAdmin.Instances.List instances =
        sqladmin.instances().list("exciting-example-123").execute();

Python

使用已获授权的 Credentials 对象调用 Google API,方法是完成 操作步骤:

  1. 为要调用的 API 构建服务对象。您构建了一个服务对象 方法是使用 API 的名称和版本以及 API 的build 已授权的 Credentials 对象。例如,要调用 Cloud SQL 管理 API:
    import googleapiclient.discovery
    
    sqladmin = googleapiclient.discovery.build('sqladmin', 'v1beta3', credentials=credentials)
  2. 使用 该服务对象提供的接口。 例如,要列出精彩示例-123 中的 Cloud SQL 数据库实例, 项目:
    response = sqladmin.instances().list(project='exciting-example-123').execute()

HTTP/REST

在您的应用获得访问令牌后,您就可以使用该令牌调用 Google API 代表指定的服务账号,或者 用户账号(如果已授予 API 所需的访问权限范围)。为此,请添加 通过添加 access_token 查询来获取对 API 的请求中的访问令牌 参数或 Authorization HTTP 标头 Bearer 值。如有可能, 最好使用 HTTP 标头,因为查询字符串通常显示在服务器日志中。大多数 可以使用客户端库来设置对 Google API 的调用(例如, 调用 Drive Files API)。

您可以访问以下网址,试用所有 Google API 并查看其作用域: OAuth 2.0 Playground

HTTP GET 示例

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

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

访问令牌过期时间

Google OAuth 2.0 授权服务器发放的访问令牌在此时间段过后过期 由 expires_in 值提供。访问令牌过期后, 应用应生成另一个 JWT,对其进行签名,并请求另一个访问令牌。

JWT 错误代码

error 字段 error_description 字段 含义 解决方法
unauthorized_client Unauthorized client or scope in request. 如果您尝试使用全网域授权功能,则相应服务账号未在 用户网域的管理控制台

确保在 管理控制台中用户的全网域授权页面, sub 项声明(字段)。

授权过程通常需要几分钟的时间,但最长可能需要 24 小时才能生效。 您的 Google 账号中的所有用户都会看到这一消息。

unauthorized_client Client is unauthorized to retrieve access tokens using this method, or client not authorized for any of the scopes requested. 服务账号是使用客户端电子邮件地址而不是客户端 ID 授权的 (数字)。 管理控制台中的全网域授权页面,移除客户端,然后重新添加 包含数字 ID。
access_denied (任意值) 如果您使用的是全网域授权功能,则请求的一个或多个范围未获得授权 管理控制台

确保在 管理控制台中用户的全网域授权页面, sub 声明(字段),并且其中包含您请求的所有范围 添加到 JWT 的 scope 声明中。

授权过程通常需要几分钟的时间,但最长可能需要 24 小时才能生效。 您的 Google 账号中的所有用户都会看到这一消息。

admin_policy_enforced (任意值) 由于 Google Workspace 管理员的政策。

参阅 Google Workspace 管理员帮助文章 控制哪些第三方和 内部应用会访问 Google Workspace 数据。 管理员可以限制对所有范围或敏感范围和受限范围的访问权限,直到 访问权限会明确授予您的 OAuth 客户端 ID。

invalid_client (任意值)

OAuth 客户端或 JWT 令牌无效或配置错误。

如需了解详情,请参阅错误说明。

请确保 JWT 令牌有效且包含正确的声明。

检查 OAuth 客户端和服务账号是否 并且您使用的是正确的电子邮件地址。

检查 JWT 令牌是否正确,以及 请求。

invalid_grant Not a valid email. 该用户不存在。 检查 sub 声明(字段)中的电子邮件地址是否正确。
invalid_grant

Invalid JWT: Token must be a short-lived token (60 minutes) and in a reasonable timeframe. Check your 'iat' and 'exp' values and use a clock with skew to account for clock differences between systems.

这通常意味着本地系统时间不正确。如果 exp 值与 iat 值之间的间隔超过 65 分钟, 或 exp 值小于 iat 值。

请确保生成 JWT 的系统上的时钟正确无误。如果 将时间与 Google NTP

invalid_grant Invalid JWT Signature.

JWT 断言使用未与服务账号关联的私钥进行签名 (由客户端电子邮件地址或所用密钥标识)已被删除、停用或 已过期。

或者,JWT 断言可能编码不正确, Base64 编码,不含换行符或填充等号。

解码 JWT 声明集并验证用于签署断言的密钥 与服务账号相关联。

尝试使用 Google 提供的 OAuth 库,确保正确生成 JWT。

invalid_scope Invalid OAuth scope or ID token audience provided. 未请求任何范围(范围列表为空),或所请求的范围之一未请求 存在(即无效)。

确保已填充 JWT 的 scope 声明(字段),并进行比较 其中包含的范围,以及您想要使用的 API 的已记录范围, 确保没有任何错误或拼写错误

请注意,scope 声明中的范围列表需要用 空格,而非逗号。

disabled_client The OAuth client was disabled. 用于对 JWT 断言进行签名的密钥已停用。

转到 Google API Console,在 IAM &管理 >服务账号,请启用包含“密钥 ID”的服务账号已使用 对断言进行签名。

org_internal This client is restricted to users within its organization. 请求中的 OAuth 客户端 ID 属于某个项目(限制对 Google 的访问) 特定 Google Cloud Organization(Google Cloud 组织)

使用组织提供的服务账号进行身份验证。确认 用户类型 配置

附录:不使用 OAuth 的服务账号授权

对于某些 Google API,您可以使用已签名的 JWT 直接作为 不记名令牌,而不是 OAuth 2.0 访问令牌。如果可以,您可以避免 在进行 API 调用之前,先向 Google 的授权服务器发出网络请求。

如果您要调用的 API 已在 Google API GitHub 代码库 您可以使用 JWT 而非访问令牌进行授权的 API 调用。为此,请执行以下操作:

  1. 按照上述说明创建服务账号。请务必 请保留您在创建账号时获得的 JSON 文件。
  2. 使用任何标准 JWT 库,例如 jwt.io,创建带有标头的 JWT 和载荷,如以下示例所示:
    {
      "alg": "RS256",
      "typ": "JWT",
      "kid": "abcdef1234567890"
    }
    .
    {
      "iss": "123456-compute@developer.gserviceaccount.com",
      "sub": "123456-compute@developer.gserviceaccount.com",
      "aud": "https://firestore.googleapis.com/",
      "iat": 1511900000,
      "exp": 1511903600
    }
    • 对于标头中的 kid 字段,请指定您服务账号的私钥 ID。您可以在服务账号的 private_key_id 字段中找到此值 JSON 文件。
    • 对于 isssub 字段,请指定您的服务账号的电子邮件地址 地址。您可以在服务的 client_email 字段中找到此值 账号 JSON 文件。
    • 对于 aud 字段,请指定 API 端点。例如:https://SERVICE.googleapis.com/
    • 对于 iat 字段,指定当前 Unix 时间, exp 字段,指定正好 3600 秒之后,即 JWT 将 过期。

使用在服务账号 JSON 文件中找到的私钥通过 RSA-256 为 JWT 签名。

例如:

Java

使用 google-api-java-clientjava-jwt:

GoogleCredential credential =
        GoogleCredential.fromStream(new FileInputStream("MyProject-1234.json"));
PrivateKey privateKey = credential.getServiceAccountPrivateKey();
String privateKeyId = credential.getServiceAccountPrivateKeyId();

long now = System.currentTimeMillis();

try {
    Algorithm algorithm = Algorithm.RSA256(null, privateKey);
    String signedJwt = JWT.create()
        .withKeyId(privateKeyId)
        .withIssuer("123456-compute@developer.gserviceaccount.com")
        .withSubject("123456-compute@developer.gserviceaccount.com")
        .withAudience("https://firestore.googleapis.com/")
        .withIssuedAt(new Date(now))
        .withExpiresAt(new Date(now + 3600 * 1000L))
        .sign(algorithm);
} catch ...

Python

使用 PyJWT

iat = time.time()
exp = iat + 3600
payload = {'iss': '123456-compute@developer.gserviceaccount.com',
           'sub': '123456-compute@developer.gserviceaccount.com',
           'aud': 'https://firestore.googleapis.com/',
           'iat': iat,
           'exp': exp}
additional_headers = {'kid': PRIVATE_KEY_ID_FROM_JSON}
signed_jwt = jwt.encode(payload, PRIVATE_KEY_FROM_JSON, headers=additional_headers,
                       algorithm='RS256')
  1. 使用已签名的 JWT 作为不记名令牌来调用 API:
    GET /v1/projects/abc/databases/123/indexes HTTP/1.1
    Authorization: Bearer SIGNED_JWT
    Host: firestore.googleapis.com

实施跨账号保护

您应采取的额外措施来保护用户的个账号实现了跨账号功能 利用 Google 的跨账号保护服务进行保护。通过这项服务 订阅安全事件通知,这些通知可向您的应用提供 关于用户账号的重大变化然后,您可以根据这些信息 您决定如何响应事件。

下面列举了一些示例来说明 Google 的跨账号保护服务发送到您应用的事件类型:

  • https://schemas.openid.net/secevent/risc/event-type/sessions-revoked
  • https://schemas.openid.net/secevent/oauth/event-type/token-revoked
  • https://schemas.openid.net/secevent/risc/event-type/account-disabled

请参阅 使用“跨账号保护”页面保护用户账号