為伺服器對伺服器應用程式採用 OAuth 2.0

透過集合功能整理內容 你可以依據偏好儲存及分類內容。
你是詳情請參閱 Google Cloud Platform 說明文件中的驗證總覽

Google OAuth 2.0 系統支援伺服器對伺服器互動,例如網頁應用程式和 Google 服務之間的互動。在本情境中,您必須使用服務帳戶,該帳戶隸屬於您的應用程式,而不是個別使用者。您的應用程式會代表服務帳戶呼叫 Google API,因此不會直接參與使用者。這種情況有時稱為「雙足式 OAuth」,或「2LO」。(相關字詞「三足式 OAuth」是指應用程式代表使用者呼叫 Google API,有時即需要使用者同意) 的情況。

一般而言,如果應用程式使用 Google API 來使用自己的資料 (而非使用者資料),則應用程式會使用服務帳戶。舉例來說,應用程式使用 Google Cloud Datastore 保存資料持續性時,會使用服務帳戶驗證對 Google Cloud Datastore API 的呼叫。

Google Workspace 網域管理員也可以授予服務帳戶整個網域的權限,以便代表網域中使用者存取使用者資料。

本文件說明如何使用 Google API 用戶端程式庫 (建議選項) 或 HTTP 來完成伺服器對伺服器 OAuth 2.0 流程。

總覽

如要支援伺服器對伺服器的互動,請先在 中為專案建立服務帳戶。如要存取 Google Workspace 使用者的使用者資料,請將全網域存取權委派給服務帳戶。

然後,您的應用程式準備好使用服務帳戶的憑證向 OAuth 2.0 驗證伺服器發出存取權杖呼叫,

最後,您的應用程式可以使用存取權杖呼叫 Google API。

建立服務帳戶

服務帳戶的憑證包含產生的不重複電子郵件地址,以及至少一組公開/私密金鑰組。如果啟用了全網域委派功能,服務帳戶 ID 也屬於服務帳戶憑證的一部分。

如果您的應用程式在 Google App Engine 上執行,則在建立專案時,會自動設定服務帳戶。

如果您的應用程式在 Google Compute Engine 上執行,系統會在您建立專案時自動設定服務帳戶,但您必須在建立 Google Compute Engine 執行個體時指定應用程式需要存取的範圍。詳情請參閱準備執行個體以使用服務帳戶一文。

如果您的應用程式沒有在 Google App Engine 或 Google Compute Engine 中執行,則必須在 中取得這些憑證。如要產生服務帳戶憑證,或查看您已產生的公開憑證,請按照下列指示操作:

First, create a service account:

  1. Open the Service accounts page.
  2. If prompted, select a project, or create a new one.
  3. Click  Create service account.
  4. Under Service account details, type a name, ID, and description for the service account, then click Create and continue.
  5. Optional: Under Grant this service account access to project, select the IAM roles to grant to the service account.
  6. Click Continue.
  7. Optional: Under Grant users access to this service account, add the users or groups that are allowed to use and manage the service account.
  8. Click Done.

Next, create a service account key:

  1. Click the email address for the service account you created.
  2. Click the Keys tab.
  3. In the Add key drop-down list, select Create new key.
  4. Click Create.

Your new public/private key pair is generated and downloaded to your machine; it serves as the only copy of the private key. You are responsible for storing it securely. If you lose this key pair, you will need to generate a new one.

您隨時可以返回 API Console 查看電子郵件地址、公開金鑰指紋和其他資訊,或是產生其他公開/私密金鑰組。如要進一步瞭解 API Console中的服務帳戶憑證,請參閱 API Console說明檔案中的服務帳戶

記下服務帳戶的電子郵件地址,並將服務帳戶私密金鑰檔案儲存在應用程式可存取的位置。您的應用程式需要這類金鑰,才能進行已授權的 API 呼叫。

將全網域授權委派給服務帳戶

如果您有 Google Workspace 帳戶,機構管理員便可授權應用程式代表 Google Workspace 網域中的使用者存取使用者資料。舉例來說,如果應用程式使用 Google Calendar API,在 Google Workspace 網域中所有使用者的日曆中新增活動,就會透過服務帳戶來代表使用者存取 Google Calendar API。授權服務帳戶代表網域使用者存取資料,有時也稱為「全網域授權」。

如要將全網域授權委派給服務帳戶,Google Workspace 網域的超級管理員必須完成下列步驟:

  1. 在 Google Workspace 網域的管理控制台中,前往「主選單」>「安全性」>「安全性」>「存取權與資料控管」>「API 控制項」
  2. 在「全網域委派」窗格中,選取「管理全網域委派設定」
  3. 按一下 [Add new] (新增)
  4. 在 [用戶端 ID] 欄位中,輸入服務帳戶和用戶端 ID。您可以在 Service accounts page中找到服務帳戶的用戶端 ID。
  5. 在 [OAuth 範圍 (以半形逗號分隔)] 欄位中,輸入應用程式應獲得存取權的範圍清單。舉例來說,如果應用程式需要全網域的完整 Google Drive API 和 Google Calendar API 存取權,請輸入:https://www.googleapis.com/auth/drive, https://www.googleapis.com/auth/calendar
  6. 按一下 [授權]。

您的應用程式現在有權向網域中的使用者發出 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("user@example.com");

在應用程式中使用 GoogleCredential 物件呼叫 Google API。

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. 委派全網域授權

    如果您已將全網域存取權委派給服務帳戶,並且想模擬使用者帳戶,請使用現有 ServiceAccountCredentials 物件的 with_subject 方法。例如:

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

使用「憑證」物件,在應用程式中呼叫 Google API。

HTTP/REST

從 API Console取得用戶端 ID 和私密金鑰後,您的應用程式需要完成下列步驟:

  1. 建立 JSON Web Token (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 標頭

標頭由兩個欄位組成,代表簽署演算法和斷言格式。這兩個欄位皆為必填欄位,而且每個欄位都只有一個值。隨著其他演算法和格式推出,這項標頭也會隨著變動。

服務帳戶必須採用 RSA SHA-256 演算法和 JWT 權杖格式。因此,標頭的 JSON 表示法如下:

{"alg":"RS256","typ":"JWT"}

此處的 Base64url 表示法如下:

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9
建立 JWT 版權聲明集

JWT 憑證集包含 JWT 的相關資訊,包括要求的權限 (範圍)、權杖的目標、核發者、核發憑證的時間和權杖的生命週期。大部分的欄位皆為必填欄位。與 JWT 標頭一樣,JWT 憑證集是 JSON 物件,用於計算簽名。

必要聲明

要求 JWT 憑證集的必要版權聲明如下所示。這些內容會以任何順序在聲明集中顯示。

名稱 說明
iss 服務帳戶的電子郵件地址。
scope 應用程式要求的權限,並以空格分隔。
aud 斷言目標的描述元。提出存取權杖要求時,這個值一律為 https://oauth2.googleapis.com/token
exp 斷言的到期時間,指定為自世界標準時間 1970 年 1 月 1 日 00:00:00 起的秒數。這個值會在核發時間算起最多 1 小時內。
iat 斷言的時間,指定為自 1970 年 1 月 1 日 00:00:00 UTC 以來的秒數。

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 安全編碼。以下是 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 Web Signature (JWS) 是一種規範,用於為 JWT 產生簽章的機制。簽名的輸入內容是下列內容的位元組陣列:

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

計算簽名時,必須使用 JWT 標頭中的簽署演算法。Google OAuth 2.0 授權伺服器僅支援 SHA-256 雜湊演算法的 RSA。系統會在 JWT 標頭的 alg 欄位中以 RS256 表示。

使用從 Google API Console取得的私密金鑰,使用 SHA256withRSA (也稱為 RSASSA-PKCS1-V1_5-SIGN 和 SHA-256 雜湊函式) 簽署輸入的 UTF-8 表示法。輸出結果是位元組陣列。

簽章必須採用 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 物件呼叫的 API 建立服務物件。例如:
    SQLAdmin sqladmin =
        new SQLAdmin.Builder(httpTransport, JSON_FACTORY, credential).build();
  2. 使用服務物件提供的介面向 API 服務發出要求。例如,如要列出惡名片-example-123 專案中的 Cloud SQL 資料庫執行個體:
    SQLAdmin.Instances.List instances =
        sqladmin.instances().list("exciting-example-123").execute();

Python

請完成下列步驟,使用已獲授權的 Credentials 物件呼叫 Google API:

  1. 為要呼叫的 API 建構服務物件。要建構服務物件,您可以呼叫 build 函式,並附上 API 名稱和授權的 Credentials 物件。例如,如要呼叫 Cloud SQL Administration API 版本 1beta3:
    import googleapiclient.discovery
    
    sqladmin = googleapiclient.discovery.build('sqladmin', 'v1beta3', credentials=credentials)
  2. 使用服務物件提供的介面向 API 服務發出要求。例如,如要列出惡名片-example-123 專案中的 Cloud SQL 資料庫執行個體:
    response = sqladmin.instances().list(project='exciting-example-123').execute()

HTTP/REST

應用程式取得存取權杖後,如果 API 已授予存取權範圍,您就可以使用該權杖代表指定的服務帳戶或使用者帳戶呼叫 Google API。方法是在向 API 的要求中加入存取權杖,方法是加入 access_token 查詢參數或 Authorization HTTP 標頭 Bearer 值。請盡可能使用 HTTP 標頭,因為查詢字串通常會顯示在伺服器記錄中。在多數情況下,您可以使用用戶端程式庫來設定對 Google API 的呼叫 (例如在呼叫 Drive API 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

存取權杖到期時

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 帳戶中的所有使用者生效。

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」的服務帳戶,以便簽署斷言。

附加條款:服務帳戶授權 (不使用 OAuth)

在某些 Google API 中,您可以直接使用已簽署的 JWT 做為不作為權杖,而不使用 OAuth 2.0 存取權杖進行授權 API 呼叫。如果可行,您就不必在向 Google 發出 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。您可以在服務帳戶 JSON 檔案的 private_key_id 欄位中找到這個值。
    • isssub 欄位中,指定服務帳戶的電子郵件地址。您可以在服務帳戶 JSON 檔案的 client_email 欄位中找到這個值。
    • 請在 aud 欄位中指定 API 端點。例如:https://SERVICE.googleapis.com/
    • 如果是 iat 欄位,請指定目前的 Unix 時間;如果是 exp 欄位,請指定確切的 JWT 過期時間的 3600 秒。

使用服務帳戶 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