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

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

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

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

替代方案

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

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

必要條件

為專案啟用 API

呼叫 Google API 的應用程式必須在 中啟用這些 API。

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

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

建立授權憑證

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

  1. 按一下「建立用戶端」
  2. 選取「電視和受限制的輸入裝置」應用程式類型。
  3. 為 OAuth 2.0 用戶端命名,然後按一下「建立」

找出存取權範圍

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

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

查看已安裝應用程式或裝置的「允許的範圍」清單。

取得 OAuth 2.0 存取權杖

即使應用程式是在輸入功能有限的裝置上執行,使用者也必須另外存取輸入功能較豐富的裝置,才能完成授權流程。流程包含下列步驟:

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

下圖說明這個程序:

使用者在有瀏覽器的其他裝置上登入

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

步驟 1:要求裝置和使用者代碼

在這個步驟中,裝置會向 Google 的授權伺服器 (位於 https://oauth2.googleapis.com/device/code) 傳送 HTTP POST 要求,識別您的應用程式,以及應用程式想代表使用者存取的存取範圍。您應使用 device_authorization_endpoint 中繼資料值,從探索文件擷取這個網址。加入下列 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 可用於取得新存取權杖的權杖。更新權杖在使用者撤銷存取權或更新權杖到期前都有效。請注意,系統一律會為裝置傳回更新權杖。
refresh_token_expires_in 重新整理權杖的剩餘有效時間 (以秒為單位)。只有在使用者授予時間限制存取權時,系統才會設定這個值。
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 要求的無效憑證。如果您要求存取與權杖相關聯的範圍,即可重新整理存取權杖,不必提示使用者授予權限 (包括使用者不在時)。

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

欄位
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 產品中授予限時存取權。例如 Data Portability API 可讓使用者一次性轉移資料。

使用者授予應用程式限時存取權時,更新權杖會在指定時間後失效。請注意,在特定情況下,重新整理權杖可能會提早失效;詳情請參閱這些案例。在這種情況下,授權碼交換回應中傳回的 refresh_token_expires_in 欄位,代表重新整理權杖到期前的剩餘時間。

導入跨帳戶防護功能

為保護使用者帳戶,您應採取額外步驟,利用 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

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