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 授權伺服器要求存取權杖,準備進行已授權的 API 呼叫。
最後,應用程式可以使用存取權杖呼叫 Google API。
建立服務帳戶
服務帳戶的憑證包括至少一組公開/私密金鑰組,以及系統產生的專屬電子郵件地址。如果啟用全網域委派功能,用戶端 ID 也會是服務帳戶憑證的一部分。
如果應用程式在 Google App Engine 上執行,系統會在您建立專案時自動設定服務帳戶。
如果應用程式是在 Google Compute Engine 上執行,系統也會在您建立專案時自動設定服務帳戶,但您必須在建立 Google Compute Engine 執行個體時,指定應用程式需要存取的範圍。詳情請參閱準備執行個體以使用服務帳戶。
如果您的應用程式不是在 Google App Engine 或 Google Compute Engine 上執行,您必須在 中取得這些憑證。如要產生服務帳戶憑證,或查看已產生的公開憑證,請按照下列步驟操作:
詳情請參閱「 管理服務帳戶金鑰的最佳做法」。
您可以隨時返回 查看電子郵件地址、公開金鑰指紋和其他資訊,或產生其他公開/私密金鑰配對。如要進一步瞭解 中的服務帳戶憑證,請參閱 說明檔案中的「服務帳戶」。
記下服務帳戶的電子郵件地址,並將服務帳戶的私密金鑰檔案儲存在應用程式可存取的位置。應用程式需要這些憑證,才能發出授權的 API 呼叫。
將全網域授權委派給服務帳戶
機構的 Workspace 管理員可以使用 Google Workspace 帳戶,授權應用程式代表 Google Workspace 網域中的使用者存取 Workspace 使用者資料。舉例來說,透過 Google Calendar API 在 Google Workspace 網域中所有使用者的日曆中加入事件的應用程式,都可以使用服務帳戶來代表這些使用者存取 Google Calendar API。這種授權服務帳戶代表所有網域使用者存取資料的方式,有時稱為「委派全網域授權」給服務帳戶。
如要將全網域權限委派給服務帳戶,Google Workspace 網域的超級管理員必須完成下列步驟:
- 在 Google Workspace 網域的 管理控制台中, 依序前往「主選單」圖示 >「安全性」> 「存取權與資料控管」>「API 控制項」。
- 在「全網域委派」窗格中,選取「管理全網域委派設定」。
- 點選「新增」。
- 在「用戶端 ID」欄位中,輸入服務帳戶的用戶端 ID。您可以在 中找到服務帳戶的用戶端 ID。
- 在「OAuth 範圍 (以半形逗號分隔)」欄位中,輸入應用程式應獲准存取的範圍清單。舉例來說,如果應用程式需要 Google Drive API 和 Google Calendar API 的全網域完整存取權,請輸入: https://www.googleapis.com/auth/drive, https://www.googleapis.com/auth/calendar。
- 按一下「授權」。
您的應用程式現在有權以 Workspace 網域中的使用者身分發出 API 呼叫 (即「模擬」使用者)。準備發出這些委派的 API 呼叫時,您會明確指定要模擬的使用者。
發出委派的 API 呼叫
Java
從 取得用戶端電子郵件地址和私密金鑰後,請使用 Java 適用的 Google Auth 程式庫,根據服務帳戶的憑證和應用程式需要存取的範圍,建立 GoogleCredentials
物件。例如:
import com.google.auth.oauth2.GoogleCredentials; import com.google.api.services.sqladmin.SQLAdminScopes; // ... GoogleCredentials credentials = GoogleCredentials.fromStream(new FileInputStream("MyProject-1234.json")) .createScoped(Collections.singleton(SQLAdminScopes.SQLSERVICE_ADMIN));
如果您在 Google Cloud 上開發應用程式,可以改用應用程式預設憑證,簡化程序。
委派全網域授權
如果您已將全網域存取權委派給服務帳戶,且想模擬使用者帳戶,請使用 GoogleCredentials
物件的 createDelegated
方法指定使用者帳戶的電子郵件地址。例如:
GoogleCredentials credentials = GoogleCredentials.fromStream(new FileInputStream("MyProject-1234.json")) .createScoped(Collections.singleton(SQLAdminScopes.SQLSERVICE_ADMIN)) .createDelegated("[email protected]");
GoogleCredentials
物件用於呼叫 createDelegated()
方法。createDelegated()
方法的引數必須是屬於您 Workspace 帳戶的使用者。發出要求的程式碼會使用這項憑證,透過服務帳戶呼叫 Google API。
Python
從 取得用戶端電子郵件地址和私密金鑰後,請使用 Python 適用的 Google API 用戶端程式庫完成下列步驟:
- 從服務帳戶的憑證和應用程式需要存取的範圍建立
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 上開發應用程式,可以改用應用程式預設憑證,簡化程序。
- 委派全網域授權
如果您已將全網域存取權委派給服務帳戶,並想模擬使用者帳戶,請使用現有
ServiceAccountCredentials
物件的with_subject
方法。例如:delegated_credentials = credentials.with_subject('[email protected]')
在應用程式中使用 Credentials 物件呼叫 Google API。
HTTP/REST
從 取得用戶端 ID 和私密金鑰後,應用程式必須完成下列步驟:
- 建立 JSON Web Token (JWT,發音為「jot」),其中包含標頭、憑證附加資訊組合和簽章。
- 向 Google OAuth 2.0 授權伺服器要求存取權杖。
- 處理授權伺服器傳回的 JSON 回應。
以下各節將說明如何完成這些步驟。
如果回應包含存取權杖,您可以使用該權杖呼叫 Google API。(如果回應未包含存取權杖,可能是 JWT 和權杖要求格式不正確,或是服務帳戶沒有權限存取要求的範圍)。
存取權杖過期時,應用程式會產生另一個 JWT、簽署該 JWT,並要求另一個存取權杖。

本節其餘部分說明如何建立 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:
- 演算法為必填欄位,且只有一個值:
"alg": "RS256"
。 - 格式為必填欄位,且只能有一個值:
"typ": "JWT"
。 - 金鑰 ID 為選用項目,是簽署 JWT 時使用的服務帳戶金鑰 ID。如果指定的金鑰 ID 不正確,系統會嘗試使用與服務帳戶相關聯的所有金鑰。如果找不到有效金鑰,系統就會拒絕符記。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 | 聲明到期時間,以自 1970 年 1 月 1 日世界標準時間 00:00:00 起算的秒數表示。這個值最晚不得超過發行時間後 1 小時。 |
iat | 聲明核發時間,以秒為單位,自世界標準時間 1970 年 1 月 1 日 00:00:00 起算。 |
以下是 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": "[email protected]", "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 簽名演算法。這項資訊會以 RS256
形式表示在 JWT 標頭的 alg
欄位中。
使用 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 後,應用程式即可使用該 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
值指定的時間範圍內重複使用。
重要安全性考量:瞭解冒用身分
委派全網域授權時,您並非直接授予服務帳戶所有使用者資料的存取權,而是授權該服務在發出 API 呼叫時,模擬特定使用者。- 代表使用者存取:您的應用程式必須為每項 API 要求指定要模擬的使用者。應用程式接著會以該特定使用者的權限運作,不會使用任何提升的權限或網域範圍的權限。
- 權限受限:服務帳戶的存取權會受到兩項因素限制:模擬使用者的權限,以及您在管理控制台中授權的 OAuth 範圍。無法存取模擬使用者本身無法存取的資料。
- 最小權限原則:這項功能允許存取使用者資料,不必取得使用者直接同意,因此請務必遵循安全性最佳做法。只授予必要的 OAuth 範圍,並確保您瞭解安全影響。
呼叫 Google API
Java
使用 GoogleCredentials
物件呼叫 Google API,請完成下列步驟:
- 使用
GoogleCredentials
物件,為要呼叫的 API 建立服務物件。例如:SQLAdmin sqladmin = new SQLAdmin.Builder(httpTransport, JSON_FACTORY, credentials).build();
- 使用服務物件提供的介面,向 API 服務提出要求。舉例來說,如要列出 exciting-example-123 專案中的 Cloud SQL 資料庫執行個體:
SQLAdmin.Instances.List instances = sqladmin.instances().list("exciting-example-123").execute();
Python
使用授權的 Credentials
物件呼叫 Google API,請完成下列步驟:
- 為要呼叫的 API 建構服務物件。您可以使用 API 的名稱和版本,以及授權的
Credentials
物件呼叫build
函式,藉此建構服務物件。舉例來說,如要呼叫 Cloud SQL Administration API 的 1beta3 版:import googleapiclient.discovery sqladmin = googleapiclient.discovery.build('sqladmin', 'v1beta3', credentials=credentials)
- 使用服務物件提供的介面,向 API 服務提出要求。舉例來說,如要列出 exciting-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 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
存取權杖過期
Google OAuth 2.0 授權伺服器核發的存取權杖會在 expires_in
值提供的時間長度後失效。存取權杖到期時,應用程式應產生另一個 JWT、簽署該權杖,然後要求另一個存取權杖。
JWT 錯誤代碼
error 欄位 | error_description 欄位 | 意義 | 如何解決 |
---|---|---|---|
unauthorized_client | Unauthorized client or scope in request. | 如果您嘗試使用全網域委派,服務帳戶未在使用者網域的管理控制台中獲得授權。 | 請確保服務帳戶已在管理控制台的「全網域委派」頁面中,獲得 授權通常會在幾分鐘內生效,但最多可能需要 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 | (任何值) | 如果您使用全網域委派,管理控制台未授權一或多個要求的範圍。 | 請確認服務帳戶已在管理控制台的「全網域委派」頁面中,為 請參閱「管理非獨立控制服務的存取權」,確認 Google 服務存取權未受限制。 授權通常會在幾分鐘內生效,但最多可能需要 24 小時,才會對 Google 帳戶中的所有使用者生效。 |
admin_policy_enforced | (任何值) | 由於 Google Workspace 管理員的政策,Google 帳戶無法授權一或多個要求範圍。 | 如要進一步瞭解管理員如何限制對所有範圍或機密/受限範圍的存取權,直到明確授予 OAuth 用戶端 ID 存取權為止,請參閱 Google Workspace 管理員說明文章「控管哪些第三方應用程式和內部應用程式可存取 Google Workspace 資料」。 |
invalid_client | (任何值) | OAuth 用戶端或 JWT 權杖無效或設定有誤。 詳情請參閱錯誤說明。 | 確認 JWT 權杖有效,且包含正確的聲明。 請確認 OAuth 用戶端和服務帳戶設定正確,且您使用的是正確的電子郵件地址。 確認 JWT 權杖正確無誤,且是為要求中的用戶端 ID 核發。 |
deleted_client | (任何值) | 用來提出要求的 OAuth 用戶端已遭刪除。如果用戶端未使用 ,系統可能會手動或自動刪除。刪除後的 30 天內均可還原用戶端。 瞭解詳情。 | 請使用仍有效的用戶端 ID。 |
invalid_grant | Not a valid email 或 Invalid email or User ID. | 使用者不存在。 | 檢查 sub 聲明 (欄位) 中的電子郵件地址是否正確。 |
invalid_grant |
| 通常表示本機系統時間不正確。如果 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 的 請注意, |
disabled_client | The OAuth client was disabled. | 用於簽署 JWT 判斷的鍵已停用。 | 前往 ,然後依序點選「IAM 與管理」>「服務帳戶」,啟用包含「金鑰 ID」的服務帳戶,該 ID 用於簽署判斷結果。 |
org_internal | This client is restricted to users within its organization. | 要求中的 OAuth 用戶端 ID 屬於專案,該專案會限制特定 Google Cloud 機構中的 Google 帳戶存取權。 | 使用機構的服務帳戶進行驗證。確認 OAuth 應用程式的使用者類型設定。 |
附錄:不使用 OAuth 的服務帳戶授權
使用部分 Google API 時,您可以直接使用已簽署的 JWT 做為不記名憑證,而不是 OAuth 2.0 存取權杖,進行授權的 API 呼叫。如果可以,您就不必在發出 API 呼叫前,向 Google 授權伺服器發出網路要求。
如果您要呼叫的 API 在 Google APIs GitHub 存放區中發布了服務定義,您可以使用 JWT 進行授權 API 呼叫,不必使用存取權杖。方法如下:
- 建立服務帳戶。請務必保留建立帳戶時取得的 JSON 檔案。
- 使用任何標準 JWT 程式庫 (例如 jwt.io 提供的程式庫),建立含有標頭和酬載的 JWT,如下列範例所示:
{ "alg": "RS256", "typ": "JWT", "kid": "abcdef1234567890" } . { "iss": "[email protected]", "sub": "[email protected]", "aud": "https://firestore.googleapis.com/", "iat": 1511900000, "exp": 1511903600 }
- 在標頭的
kid
欄位中,指定服務帳戶的私密金鑰 ID。您可以在服務帳戶 JSON 檔案的private_key_id
欄位中找到這個值。 - 在
iss
和sub
欄位中,指定服務帳戶的電子郵件地址。您可以在服務帳戶 JSON 檔案的client_email
欄位中找到這個值。這個值可專門識別用戶端,在功能上是用戶端 ID。 - 在
aud
欄位中,指定 API 端點。例如:https://SERVICE.googleapis.com/
。 - 針對
iat
欄位,請指定目前的 Unix Epoch 時間;針對exp
欄位,請指定 3600 秒後的確切時間,屆時 JWT 將過期。
使用服務帳戶 JSON 檔案中的私密金鑰,以 RSA-256 簽署 JWT。
例如:
Java
使用 google-auth-library-java 和 java-jwt:
import com.google.auth.oauth2.ServiceAccountCredentials; ... GoogleCredentials credentials = GoogleCredentials.fromStream(new FileInputStream("MyProject-1234.json")); PrivateKey privateKey = ((ServiceAccountCredentials) credentials).getPrivateKey(); String privateKeyId = ((ServiceAccountCredentials) credentials).getPrivateKeyId(); long now = System.currentTimeMillis(); try { Algorithm algorithm = Algorithm.RSA256(null, privateKey); String signedJwt = JWT.create() .withKeyId(privateKeyId) .withIssuer("[email protected]") .withSubject("[email protected]") .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': '[email protected]', 'sub': '[email protected]', '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')
- 使用已簽署的 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
如要進一步瞭解如何實作跨帳戶保護機制,以及查看可用事件的完整清單,請參閱「 透過跨帳戶保護機制保護使用者帳戶 」頁面。