電視和限縮裝置應用程式專用的 OAuth 2.0

本文說明如何實作 OAuth 2.0 授權,以便透過電視、遊戲主機和印表機等裝置上執行的應用程式存取 Google API。更明確地說,此流程適用於沒有瀏覽器存取權或輸入功能有限的裝置。

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

由於使用此流程的應用程式會發送到個別裝置,因此假設應用程式無法保存密鑰。當使用者在應用程式中執行,或於背景執行時,使用者便可存取 Google API。

替代方案

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

如果您「只想」透過 Google 帳戶登入使用者,並使用 JWT ID 憑證來取得基本使用者設定檔資訊,請參閱使用電視和受限制的輸入裝置登入

必要條件

為您的專案啟用 API

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

為專案啟用 API:

  1. Open the API Library 在 Google API Console內。
  2. If prompted, select a project, or create a new one.
  3. API Library 會列出所有可用的 API,並依照產品系列和熱門程度分組。如果您要啟用的 API 並未出現在清單中,請使用搜尋功能尋找該 API,或是在其所屬的產品系列中按一下 [查看全部]
  4. 選取您要啟用的 API,然後按一下 [啟用] 按鈕。
  5. If prompted, enable billing.
  6. If prompted, read and accept the API's Terms of Service.

建立授權憑證

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

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

識別存取權範圍

範圍可讓應用程式只要求存取所需的資源,同時允許使用者控管自己授予應用程式的存取權數量。因此,要求的範圍數量與取得使用者同意的可能性可能會有相反的關係。

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

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

取得 OAuth 2.0 存取憑證

即使您的應用程式是在具備有限輸入功能的裝置上執行,使用者仍必須具有獨立輸入裝置的存取權限,以完成這項授權流程。流程如下:

  1. 您的應用程式會向 Google 的授權伺服器傳送要求,指出您的應用程式會要求存取的範圍。
  2. 伺服器傳回一些資訊,用於後續步驟 (例如裝置代碼和使用者代碼)。
  3. 顯示的資訊可以顯示在其他裝置上,以授權給您的應用程式。
  4. 您的應用程式會開始輪詢 Google 的授權伺服器,確認使用者是否授權您的應用程式。
  5. 使用者改用了功能更豐富的輸入裝置、啟動網路瀏覽器、瀏覽至步驟 3 顯示的網址,然後輸入在步驟 3 顯示的程式碼。接著,使用者就能授予 (或拒絕) 存取應用程式。
  6. 下一次對意見調查要求的回應包含應用程式需要代表使用者授權的要求憑證。(如果使用者拒絕存取您的應用程式,回應中不會包含憑證)。

下圖說明這個程序:

使用者在設有瀏覽器的另一部裝置上登入

以下各節詳細說明瞭這些步驟。基於裝置可能具備的功能範圍和執行階段環境,本文提供的範例使用 curl 指令列公用程式。這些範例應該很容易就能轉移至不同的語言和執行階段。

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

在這個步驟中,您的裝置會將 HTTP POST 要求傳送至 https://oauth2.googleapis.com/device/code 的授權伺服器,此伺服器會指明您的應用程式,以及應用程式要代表使用者存取的存取權範圍。您必須從 Google 探索文件 使用 device_authorization_endpoint 中繼資料值擷取這個網址。包含下列 HTTP 要求參數:

參數
client_id 必要

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

scope 必要

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

範圍可讓應用程式只要求存取所需資源的存取權,同時也能控管使用者授予應用程式存取權的權限。因此,要求的範圍數量與取得使用者同意的可能性之間存在逆向關係。

範例

下列程式碼片段顯示要求範例:

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

client_id=client_id&scope=

這個範例顯示傳送相同要求的 curl 指令:

curl -d "client_id=client_id&scope=" \
     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。您可以在 API ConsoleCredentials page中找到這個值。
client_secret 所提供 client_id 的用戶端密鑰。您可以在 API ConsoleCredentials page中找到這個值。
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" \
         /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 狀態碼 說明
invalid_client 401 找不到 OAuth 用戶端。舉例來說,如果 client_id 參數值無效,就會發生這個錯誤。
invalid_grant 400 code 參數值無效。
unsupported_grant_type 400 grant_type 參數值無效。

呼叫 Google API

在應用程式取得存取憑證後,如果系統授予了 API 所需的存取權範圍,您就可以使用這個符記代表特定使用者帳戶呼叫 Google API。方法是加入 API 要求,加入 access_token 查詢參數或 Authorization HTTP 標頭 Bearer 值。可以的話,建議使用 HTTP 標頭,因為查詢字串通常會顯示在伺服器記錄中。在大多數情況下,您都可以使用用戶端程式庫來設定對 Google API 的呼叫 (例如在呼叫 Drive Files API 時)。

您可以試用所有 Google API,並在 OAuth 2.0 Playground 中查看其範圍。

HTTP GET 範例

使用 Authorization: Bearer HTTP 標頭呼叫 drive.files 端點 (Drive API 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 API Console取得的用戶端 ID。
client_secret API Console取得的用戶端密鑰。
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",
  "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