針對伺服器對伺服器應用程式使用 OAuth 2.0

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

總覽

如要支援伺服器對伺服器的互動,請先在 API Console。如果要存取 並委派全網域存取權給服務帳戶。

接著,應用程式會使用服務帳戶的 憑證,要求 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 呼叫

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

使用 Google Workspace 帳戶,機構的 Workspace 管理員可以授權 應用程式,代表 Google Workspace 網域中的使用者存取 Workspace 使用者資料。例如: 應用程式,可透過 Google Calendar API 將活動新增至貴機構所有使用者的行事曆 Google Workspace 網域可透過服務帳戶存取 服務等級目標授權服務帳戶代表網域中使用者存取資料, 有時也稱為「委派全網域授權」服務帳戶

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

  1. 來自您 Google Workspace 網域的 管理控制台,前往主選單 >安全性 > 存取權與資料控管 >API 控制項
  2. 在「全網域委派」窗格中,選取「管理全網域委派」
  3. 按一下 [Add new] (新增)
  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

從 Cloud Shell 伺服器 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 和 API。

Python

從 Cloud Shell 伺服器 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

從 Cloud 控制台取得用戶端 ID 和私密金鑰 API Console,您的應用程式必須完成 步驟如下:

  1. 建立 JSON Web Token (JWT,發音為「jot」),其中包含標頭、憑證附加資訊集 加上一個簽名
  2. 向 Google OAuth 2.0 授權伺服器要求存取權杖。
  3. 處理授權伺服器傳回的 JSON 回應。

以下各節說明如何完成這些步驟。

如果回應中含有存取權杖,您可以使用存取權杖 呼叫 Google API。(如果回覆不含 您的 JWT 和權杖要求可能格式不正確,或者服務帳戶 您沒有要求範圍的存取權限。)

當存取權杖過期時,應用程式會產生另一個 JWT 並進行簽署,然後要求另一個存取權杖。

您的伺服器應用程式會使用 JWT,向 Google
                  Authorization Server,然後使用權杖呼叫 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 斷言的到期時間,以世界標準時間 00:00:00 之後的秒數表示 1970 年 1 月 1 日。這個值最多不超過發出時間的 1 小時。
iat 發出斷言的時間,以世界標準時間 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 授權伺服器支援的簽署演算法是 RSA 使用 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 後,應用程式即可使用該 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. 使用 服務物件提供的介面。 舉例來說,假設您要列出 android-example-123 中的 Cloud SQL 資料庫執行個體 專案:
    SQLAdmin.Instances.List instances =
        sqladmin.instances().list("exciting-example-123").execute();

Python

完成下方步驟,使用授權的 Credentials 物件呼叫 Google API 步驟如下:

  1. 為您要呼叫的 API 建立服務物件。您會建構服務物件 方法是使用 API 名稱和版本,呼叫 build 函式。 已授權的 Credentials 物件。舉例來說,如要呼叫 Cloud SQL 管理 API:
    import googleapiclient.discovery
    
    sqladmin = googleapiclient.discovery.build('sqladmin', 'v1beta3', credentials=credentials)
  2. 使用 服務物件提供的介面。 舉例來說,假設您要列出 android-example-123 中的 Cloud SQL 資料庫執行個體 專案:
    response = sqladmin.instances().list(project='exciting-example-123').execute()

HTTP/REST

應用程式取得存取權杖後,您可以使用該權杖向 Google 或 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: BearerHTTP 標題可能如下所示。請注意,您必須指定自己的存取權杖:

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 聲明 (欄位),包含您要求的所有範圍 加到 scope JWT 憑證附加資訊中。

這項作業通常會在幾分鐘內完成,但系統最多需要 24 小時才能完成驗證程序 將套用到您 Google 帳戶中的所有使用者。

admin_policy_enforced (任何值) Google 帳戶無法授權一或多個要求的範圍,原因如下: Google Workspace 管理員的政策

參閱 Google Workspace 管理員說明文章 控管哪些第三方和 內部應用程式存取 Google Workspace 資料,進一步瞭解 管理員可能會限制所有範圍或敏感和受限制範圍的存取權,直到 明確授予 OAuth 用戶端 ID 的存取權。

invalid_client (任何值)

OAuth 用戶端或 JWT 權杖無效或設定有誤。

詳情請參閱錯誤說明。

請確定 JWT 權杖有效且包含正確的憑證附加資訊。

請確認 OAuth 用戶端和服務帳戶 並且使用正確的電子郵件地址。

請確認 JWT 權杖是否正確,且已核發給用戶端 ID 請求。

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 機構

請使用機構中的服務帳戶進行驗證。確認 使用者類型 設定

附加條款:沒有 OAuth 的服務帳戶授權

您可以在部分 Google API 中,直接使用已簽署的 JWT 做為授權的 API 呼叫, 而非 OAuth 2.0 存取權杖。只要可行,您就能避免 先向 Google 的授權伺服器發出網路要求,再發出 API 呼叫。

如果您要呼叫的 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 欄位,指定服務帳戶的私密金鑰 編號。您可以在服務帳戶的 private_key_id 欄位中找到這個值 JSON 檔案
    • isssub 欄位中,指定服務帳戶的電子郵件 讓我們看看 DNS 解析 進一步探索內部和外部位址您可以在服務的 client_email 欄位找到這個值 帳戶 JSON 檔案
    • aud 欄位中,指定 API 端點。例如: https://SERVICE.googleapis.com/
    • iat 欄位中,指定目前的 Unix 時間,以及 exp 欄位,指定 3,600 秒之後的時間,也就是 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

詳情請參閱 透過跨帳戶防護頁面保護使用者帳戶 ,進一步瞭解如何導入跨帳戶防護功能,以及查看可用事件的完整清單。