適用於電視和有限輸入裝置應用程式的 OAuth 2.0

本文件說明如何實作 OAuth 2.0 授權,透過在電視、遊戲主機和印表機等裝置上執行的應用程式存取 Google API。具體來說,這個流程專為無法存取瀏覽器或輸入功能受限的裝置而設計。

OAuth 2.0 可讓使用者與應用程式共用特定資料,同時保有使用者名稱、密碼和其他資訊的隱私。舉例來說,電視應用程式可以使用 OAuth 2.0 取得權限,選取儲存在 Google 雲端硬碟中的檔案。

由於使用這個流程的應用程式會分發至個別裝置,因此假設這些應用程式無法保密。使用者在應用程式中,或應用程式在背景執行時,都可以存取 Google API。

替代方案

如果您要為 Android、iOS、macOS、Linux 或 Windows (包括通用 Windows 平台) 等平台編寫應用程式,且該應用程式可存取瀏覽器和完整輸入功能,請使用行動和電腦應用程式的 OAuth 2.0 流程。(即使應用程式是沒有圖形介面的指令列工具,也應使用該流程)。

如果您想讓使用者透過 Google 帳戶登入,並使用 JWT ID 權杖取得基本使用者個人資料,請參閱「在電視和輸入功能有限的裝置上登入」。

必要條件

為專案啟用 API

任何會呼叫 Google API 的應用程式,都必須在 中啟用這些 API。

如要為專案啟用 API,請按照下列步驟操作:

  1. 在 中。
  2. 會列出所有可用的 API,並按照產品系列和熱門程度分組。如果清單裡找不到您想啟用的 API,請使用搜尋功能,或在該 API 所屬的產品系列中按一下「查看全部」
  3. 選取要啟用的 API,然後按一下「啟用」按鈕。

建立授權憑證

任何使用 OAuth 2.0 存取 Google API 的應用程式,都必須具備授權憑證,才能向 Google 的 OAuth 2.0 伺服器識別應用程式。下列步驟說明如何為專案建立憑證。應用程式就能使用憑證存取您為該專案啟用的 API。

  1. 按一下 [Create credentials] (建立憑證) > [OAuth client ID] (OAuth 用戶端 ID)
  2. 選取「電視和受限制的輸入裝置」應用程式類型。
  3. 為 OAuth 2.0 用戶端命名,然後按一下「建立」

找出存取權範圍

範圍可讓應用程式僅要求存取其需要的資源,也能讓使用者控制對應用程式授予的存取量。因此,要求的範圍數量與取得使用者同意的可能性之間可能呈現反比關係。

開始實作 OAuth 2.0 授權之前,建議您找出應用程式需要權限存取的範圍。

請參閱已安裝應用程式或裝置的允許範圍清單。

取得 OAuth 2.0 存取權杖

即使應用程式是在輸入功能受限的裝置上執行,使用者仍必須透過具備更豐富輸入功能的裝置,才能完成這項授權流程。流程包含以下步驟:

  1. 應用程式會向 Google 授權伺服器傳送要求,指出應用程式會要求存取哪些權限範圍。
  2. 伺服器會傳回幾個在後續步驟中使用的資訊,例如裝置代碼和使用者代碼。
  3. 您可以顯示使用者可在其他裝置上輸入的資訊,以便授權應用程式。
  4. 應用程式會開始輪詢 Google 授權伺服器,判斷使用者是否已授權您的應用程式。
  5. 使用者切換至輸入功能更豐富的裝置,開啟網路瀏覽器,前往步驟 3 顯示的網址,然後輸入步驟 3 中顯示的程式碼。使用者就能授予 (或拒絕) 應用程式的存取權。
  6. 輪詢要求的下一個回應會包含應用程式需要的權杖,以便代表使用者授權要求。(如果使用者拒絕存取您的應用程式,回應就不會包含權杖)。

下圖說明這項程序:

使用者在裝置上登入瀏覽器

以下各節將詳細說明這些步驟。考量裝置可能具備的功能和執行階段環境,本文件中的範例會使用 curl 指令列公用程式。這些範例應可輕鬆移植至各種語言和執行階段。

步驟 1:索取裝置和使用者代碼

在這個步驟中,裝置會在 https://oauth2.googleapis.com/device/code 傳送 HTTP POST 要求給 Google 授權伺服器,以便識別應用程式,以及應用程式要代使用者存取的存取權範圍。您應使用 device_authorization_endpoint 中繼資料值,從Discovery 文件擷取這個網址。加入下列 HTTP 要求參數:

參數
client_id 必填

應用程式的用戶端 ID。您可以在 中找到這個值。

scope 必填

以空格分隔的範圍清單,用於識別應用程式可代表使用者存取的資源。這些值會提供 Google 向使用者顯示的同意畫面。請參閱已安裝應用程式或裝置的允許範圍清單。

範圍可讓應用程式僅要求存取其需要的資源,也能讓使用者控制對應用程式授予的存取量。因此,要求的範圍數量與取得使用者同意的可能性之間存在反比關係。

範例

以下程式碼片段為要求範例:

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

client_id=client_id&scope=email%20profile

以下範例顯示 curl 指令,可用於傳送相同要求:

curl -d "client_id=client_id&scope=email%20profile" \
     https://oauth2.googleapis.com/device/code

步驟 2:處理授權伺服器回應

授權伺服器會傳回下列其中一項回應:

成功回應

如果要求有效,回應會是 JSON 物件,其中包含下列屬性:

屬性
device_code Google 專屬指派的值,用於識別執行要求授權應用程式的裝置。使用者會透過另一台具備更豐富輸入功能的裝置授權該裝置。舉例來說,使用者可能會使用筆電或手機授權在電視上執行的應用程式。在本例中,device_code 會識別電視。

這段程式碼可讓執行應用程式的裝置安全地判斷使用者是否已授予或拒絕存取權。

expires_in device_codeuser_code 的有效時間長度 (以秒為單位)。如果使用者未在該時間內完成授權流程,且裝置也未輪詢以擷取使用者決定的相關資訊,您可能需要從步驟 1 重新開始這個程序。
interval 裝置在兩次輪詢要求之間應等待的時間長度 (以秒為單位)。舉例來說,如果值為 5,裝置應每五秒向 Google 授權伺服器傳送一次輪詢要求。詳情請參閱步驟 3
user_code 這是一個區分大小寫的值,可向 Google 指出應用程式要求存取的範圍。您的使用者介面會指示使用者在具備更豐富輸入功能的其他裝置上輸入這個值。接著,Google 會在提示使用者授予應用程式存取權時,使用該值顯示正確的權限集。
verification_url 使用者必須在其他裝置上前往這個網址,才能進入 user_code,並授予或拒絕應用程式的存取權。您的使用者介面也會顯示這個值。

以下程式碼片段為回應範例:

{
  "device_code": "4/4-GMMhmHCXhWEzkobqIHGG_EnNYYsAkukHspeYUk9E8",
  "user_code": "GQVQ-JKEC",
  "verification_url": "https://www.google.com/device",
  "expires_in": 1800,
  "interval": 5
}

超過配額的回應

如果裝置程式碼要求超出與用戶端 ID 相關聯的配額,您會收到 403 回應,其中包含以下錯誤:

{
  "error_code": "rate_limit_exceeded"
}

在這種情況下,請使用輪詢策略來降低要求頻率。

步驟 3:顯示使用者代碼

向使用者顯示在步驟 2 中取得的 verification_urluser_code。這兩個值可包含 US-ASCII 字元集的任何可列印字元。您向使用者顯示的內容應會指示使用者前往另一部裝置上的 verification_url,並輸入 user_code

設計使用者介面 (UI) 時,請遵守下列規則:

  • user_code
    • user_code 必須顯示在可處理 15 個「W」大小字元的欄位中。換句話說,如果您可以正確顯示程式碼 WWWWWWWWWWWWWWW,表示您的 UI 有效,建議您在測試 user_code 在 UI 中顯示的方式時,使用該字串值。
    • user_code 區分大小寫,且不得以任何方式修改,例如變更大小寫或插入其他格式化字元。
  • verification_url
    • 顯示 verification_url 的空間必須足夠寬,才能處理長度為 40 個半形字元的網址字串。
    • 您不應以任何方式修改 verification_url,但可視需要移除顯示用的配置。如果您打算基於顯示原因,從網址中移除配置 (例如 https://),請務必確保應用程式能同時處理 httphttps 變化版本。

步驟 4:輪詢 Google 授權伺服器

由於使用者會使用其他裝置前往 verification_url 並授予 (或拒絕) 存取權,因此當使用者回應存取權要求時,要求裝置不會自動收到通知。因此,要求裝置需要輪詢 Google 授權伺服器,以判斷使用者何時回應要求。

要求裝置應持續傳送輪詢要求,直到收到指出使用者已回應存取要求的回應,或在 步驟 2中取得的 device_codeuser_code 到期為止。步驟 2 中傳回的 interval 會指定要求之間的等待時間長度 (以秒為單位)。

要輪詢的端點網址為 https://oauth2.googleapis.com/token。輪詢要求包含下列參數:

參數
client_id 應用程式的用戶端 ID。您可以在 中找到這個值。
client_secret 提供的 client_id 用戶端密鑰。您可以在 中找到這個值。
device_code 授權伺服器在步驟 2 中傳回的 device_code
grant_type 將這個值設為 urn:ietf:params:oauth:grant-type:device_code

範例

以下程式碼片段為要求範例:

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

client_id=client_id&
client_secret=client_secret&
device_code=device_code&
grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Adevice_code

以下範例顯示 curl 指令,可用於傳送相同要求:

curl -d "client_id=client_id&client_secret=client_secret& \
         device_code=device_code& \
         grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Adevice_code" \
         -H "Content-Type: application/x-www-form-urlencoded" \
         https://oauth2.googleapis.com/token

步驟 5:使用者回應存取權要求

下圖顯示的頁面與步驟 3 中顯示的 verification_url 類似,使用者瀏覽該頁面時會看到類似的內容:

輸入代碼連結裝置

輸入 user_code 後,如果使用者尚未登入 Google,系統會要求他們登入,並顯示同意聲明畫面,如下所示:

裝置用戶端的同意畫面範例

步驟 6:處理輪詢要求的回應

Google 授權伺服器會針對每項輪詢要求回應以下任一回應:

已授予存取權

如果使用者授予裝置存取權 (在同意聲明畫面中按一下 Allow),回應就會包含存取權杖和重新整理權杖。憑證可讓裝置代表使用者存取 Google API。(回應中的 scope 屬性會決定裝置可存取哪些 API)。

在這種情況下,API 回應會包含下列欄位:

欄位
access_token 應用程式傳送的權杖,用於授權 Google API 要求。
expires_in 存取權杖的剩餘生命週期 (以秒為單位)。
refresh_token 您可以使用這項權杖取得新的存取權杖。重新整理權杖在使用者撤銷存取權之前有效。請注意,系統一律會針對裝置傳回更新權杖。
scope access_token 授予的存取範圍,以空格分隔的字串清單表示,並區分大小寫。
token_type 傳回的符記類型。此時,這個欄位的值一律會設為 Bearer

以下程式碼片段為回應範例:

{
  "access_token": "1/fFAGRNJru1FTz70BzhT3Zg",
  "expires_in": 3920,
  "scope": "openid https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email",
  "token_type": "Bearer",
  "refresh_token": "1/xEoDL4iW3cxlI7yDbSRFYNG01kVKM2C-259HOF2aQbI"
}

存取權杖的有效期限有限。如果應用程式需要長時間存取 API,可以使用重新整理權杖取得新的存取權杖。如果應用程式需要這類存取權,則應儲存重新整理權杖,以供日後使用。

存取遭拒

如果使用者拒絕授予裝置存取權,伺服器回應就會包含 403 HTTP 回應狀態碼 (Forbidden)。回應中會包含以下錯誤:

{
  "error": "access_denied",
  "error_description": "Forbidden"
}

授權尚未完成

如果使用者尚未完成授權流程,伺服器會傳回 428 HTTP 回應狀態碼 (Precondition Required)。回應中包含以下錯誤:

{
  "error": "authorization_pending",
  "error_description": "Precondition Required"
}

檢查頻率過高

如果裝置傳送的輪詢要求過於頻繁,伺服器會傳回 403 HTTP 回應狀態碼 (Forbidden)。回應包含以下錯誤:

{
  "error": "slow_down",
  "error_description": "Forbidden"
}

其他錯誤

如果輪詢要求缺少任何必要參數或參數值不正確,授權伺服器也會傳回錯誤。這些要求通常會包含 400 (Bad Request) 或 401 (Unauthorized) HTTP 回應狀態碼。這些錯誤包括:

錯誤 HTTP 狀態碼 說明
admin_policy_enforced 400 由於 Google Workspace 管理員的政策,Google 帳戶無法授權所要求的一或多個範圍。如要進一步瞭解管理員如何限制範圍存取權,直到明確授予 OAuth 客戶端 ID 存取權為止,請參閱 Google Workspace 管理員說明文章「控管哪些第三方應用程式和內部應用程式可存取 Google Workspace 資料」。
invalid_client 401

找不到 OAuth 用戶端,舉例來說,如果 client_id 參數值無效,就會發生這項錯誤。

OAuth 用戶端類型不正確。請確認客戶端 ID 的應用程式類型已設為「電視和受限制的輸入裝置」

invalid_grant 400 code 參數值無效、已聲明或無法剖析。
unsupported_grant_type 400 grant_type 參數值無效。
org_internal 403 要求中的 OAuth 用戶端 ID 是專案的一部分,可限制特定 Google Cloud 機構中的 Google 帳戶存取權。確認 OAuth 應用程式的使用者類型設定

呼叫 Google API

應用程式取得存取權權杖後,如果已授予 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

重新整理存取權杖

存取權杖會定期到期,並成為相關 API 要求的無效憑證。如果您要求與權杖相關聯的範圍的離線存取權,則可以重新整理存取權杖,而無須提示使用者授予權限 (包括使用者不在場時)。

如要重新整理存取權杖,應用程式會向 Google 的授權伺服器 (https://oauth2.googleapis.com/token) 傳送 HTTPS POST 要求,其中包含下列參數:

欄位
client_id 從 取得的用戶端 ID。
client_secret 從 取得的用戶端密鑰。
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 https://www.googleapis.com/auth/calendar.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 和錯誤代碼。

允許的範圍

裝置專用的 OAuth 2.0 流程僅支援下列範圍:

OpenID ConnectGoogle 登入

  • email
  • openid
  • profile

Drive API

  • https://www.googleapis.com/auth/drive.appdata
  • https://www.googleapis.com/auth/drive.file

YouTube API

  • https://www.googleapis.com/auth/youtube
  • https://www.googleapis.com/auth/youtube.readonly

導入跨帳戶防護

您還可以透過 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

如要進一步瞭解如何實作跨帳戶保護功能,以及查看可用的完整事件清單,請參閱「 透過跨帳戶保護功能保護使用者帳戶 」一文。