導入 OAuth 2.0 伺服器

每個 smart home 動作都必須包含驗證使用者的機制。

驗證功能可讓您將使用者的 Google 帳戶連結至驗證系統的使用者帳戶。如此一來,您就能在執行要求收到智慧住宅意圖時識別使用者。Google 智慧型住宅僅支援透過授權碼流程的 OAuth。

本頁說明如何設定 OAuth 2.0 伺服器,以便與 smart home 動作搭配運作。

使用 OAuth 連結 Google 帳戶

授權碼流程中,您需要兩個端點:

  • 授權端點:會向尚未登入的使用者顯示登入 UI。授權端點也會建立短期授權碼,記錄使用者對於所要求存取權的同意。

  • 權杖交換端點,負責兩種交換類型:

    1. 交換長期更新權杖和短期存取權杖的授權碼。這個交換作業會在使用者進行帳戶連結流程時發生。
    2. 將長效更新權杖交換為短期存取權杖。當 Google 需要新的存取權杖而因過期而需要新的存取權杖時,就會發生這個交換作業。

設計指南

本節說明針對您代管 OAuth 連結流程的使用者畫面設計規定和建議。Google 應用程式呼叫完畢後,您的平台便會向使用者顯示登入 Google 頁面和帳戶連結同意畫面。使用者同意連結帳戶後,系統就會將使用者導回 Google 應用程式。

這個圖顯示使用者將 Google 帳戶連結至驗證系統的步驟。第一張螢幕截圖顯示使用者從您的平台啟動的連結。第二張圖片顯示了使用者登入 Google,第三張圖片則顯示使用者同意,並確認將 Google 帳戶與應用程式連結。最後螢幕截圖則顯示 Google 應用程式中的連結成功使用者帳戶。
圖 1.帳戶連結使用者登入 Google 和同意畫面。

需求條件

  1. 您必須說明使用者帳戶會連結至 Google,而「不是」連結至特定 Google 產品 (例如 Google Home 或 Google 助理)。
  2. 您必須擁有 Google 授權聲明,例如「登入,即表示您授權 Google 控制您的裝置」。請參閱《Google Home 開發人員政策》的「Google 裝置控制授權」一節。
  3. 當使用者選擇不連結時,您必須提供返回或取消連結的方式。
  4. 您必須開啟 Web OAuth 連結頁面,並確保使用者可透過清楚的方式登入 Google 帳戶,例如使用者名稱和密碼的欄位。請勿使用 Google 登入 (GSI) 方法,讓使用者不必將他們帶往網頁 OAuth 連結頁面即可進行連結。違反了 Google 政策。

建議

建議您採取下列做法:

  1. 顯示《Google 隱私權政策》。在同意畫面中加入《Google 隱私權政策》連結。

  2. 要分享的資料。以簡明扼要的用語告知使用者 Google 需要哪些資料以及要求的原因。

  3. 明確的行動號召。在同意畫面中顯示明確的行動號召,例如「同意並連結」。這是因為使用者必須瞭解必須和 Google 分享哪些資料才能連結帳戶。

  4. 可取消連結功能。提供讓使用者取消連結的機制,例如在您平台的帳戶設定網址。您也可以加入 Google 帳戶連結,方便使用者管理已連結的帳戶。

  5. 可變更使用者帳戶。建議使用者切換帳戶的方法。如果使用者常擁有多個帳戶,這種做法尤其實用。

    • 如果使用者必須關閉同意畫面才能切換帳戶,請傳送可復原的錯誤給 Google,讓使用者以 OAuth 連結登入所需帳戶。
  6. 加入您的標誌。在同意畫面中顯示貴公司的標誌。 參考樣式規範來放置標誌。如果您也想顯示 Google 的標誌,請參閱「標誌和商標」一文。

授權碼流程

「授權碼」流程的 OAuth 2.0 伺服器實作包含兩個端點,您的服務可透過 HTTPS 提供。第一個端點是授權端點,負責尋找及存取資料,或取得使用者同意聲明。授權端點會將登入使用者介面提供給尚未登入的使用者,並記錄同意要求的存取權。第二個端點是權杖交換端點,可用於取得加密字串 (稱為「權杖」),授權使用者存取服務。

當 Google 應用程式需要呼叫您其中一個服務的 API 時,Google 會同時使用這些端點,取得您使用者授權來呼叫這些 API 的權限。

Google 發起的 OAuth 2.0 授權碼流程流程如下:

  1. Google 會在使用者的瀏覽器中開啟授權端點。如果流程是在語音專用裝置上執行動作,Google 會將執行作業轉移到手機。
  2. 如果使用者尚未登入,系統會登入,並授予 Google 透過您的 API 存取其資料的權限;如果使用者尚未授予權限,則會授權 Google。
  3. 您的服務會建立授權碼,然後傳送給 Google。如要執行此操作,請將使用者瀏覽器重新導向至 Google,並在要求中附上授權碼。
  4. Google 會將授權碼傳送至您的權杖交換端點,這個程式碼會驗證程式碼的真實性,並傳回存取權杖更新權杖。存取權杖是一種短期權杖,讓您的服務接受以存取 API 的憑證。更新權杖是一種長期權杖,當 Google 到期時,Google 可以用來儲存新的存取權杖。
  5. 使用者完成帳戶連結流程後,Google 傳送的所有後續要求都含有存取權杖。

處理授權要求

如果您需要使用 OAuth 2.0 授權碼流程執行帳戶連結,Google 會傳送要求到授權端點,其中包含包含下列參數的要求:

授權端點參數
client_id 您指派給 Google 的用戶端 ID。
redirect_uri 將回應傳送至這個請求的網址。
state 將傳回 Google 的記帳值在重新導向 URI 中維持不變。
scope 選用:以空格分隔的一組範圍字串,用於指定 Google 要求授權的資料。
response_type 回應中要傳回的值類型。如果是 OAuth 2.0 授權流程流程,回應類型一律為 code
user_locale 採用 RFC5646 格式的 Google 帳戶語言設定,以便讓使用者以偏好的語言將內容本地化。

例如,如果您的授權端點位於 https://myservice.example.com/auth,則要求可能如下所示:

GET https://myservice.example.com/auth?client_id=GOOGLE_CLIENT_ID&redirect_uri=REDIRECT_URI&state=STATE_STRING&scope=REQUESTED_SCOPES&response_type=code&user_locale=LOCALE

為了讓授權端點處理登入要求,請按照下列步驟操作:

  1. 驗證 client_id 與指派給 Google 的用戶端 ID 相符,且 redirect_uri 與 Google 為您的服務提供的重新導向網址相符。這類檢查作業可防止他人存取非預期或設定錯誤的用戶端應用程式。如果您支援多個 OAuth 2.0 流程,請確認 response_typecode
  2. 檢查使用者是否已登入服務。如果使用者未登入,請完成服務的登入或註冊流程。
  3. 產生授權碼,讓 Google 存取您的 API。授權碼可以是任何字串值,但必須代表使用者、權杖所屬的用戶端和代碼的到期時間,且不得猜測。系統通常會提供大約 10 分鐘後到期的授權碼。
  4. 確認 redirect_uri 參數指定的網址格式如下:
      https://oauth-redirect.googleusercontent.com/r/YOUR_PROJECT_ID
      https://oauth-redirect-sandbox.googleusercontent.com/r/YOUR_PROJECT_ID
      
  5. 將使用者的瀏覽器重新導向至 redirect_uri 參數指定的網址。附上 codestate 參數,在重新導向時加上您剛剛產生的授權碼及原始的未修改狀態值。以下是結果網址的範例:
    https://oauth-redirect.googleusercontent.com/r/YOUR_PROJECT_ID?code=AUTHORIZATION_CODE&state=STATE_STRING

處理權杖交換要求

您的服務權杖交換端點會負責兩種權杖交換作業:

  • 取得存取權杖和更新權杖的授權碼
  • 交換存取權杖的更新權杖

權杖交換要求包含下列參數:

權杖交換端點參數
client_id 可識別要求來源為 Google 的字串。這個字串必須在您的系統中註冊為 Google #39; 專屬 ID。
client_secret 您向 Google 註冊的服務的密鑰字串。
grant_type 交換的權杖類型。該屬性為 authorization_coderefresh_token
code grant_type=authorization_code 時,這個參數是指 Google 從您的登入或權杖交換端點收到的代碼。
redirect_uri 使用 grant_type=authorization_code 時,這個參數是初始授權要求中使用的網址。
refresh_token grant_type=refresh_token 時,這個參數是指 Google 從您的權杖交換端點收到的更新權杖。

取得存取權杖和更新權杖的授權碼

使用者登入後,您的授權端點會將短期的授權代碼回傳給 Google,Google 會向權杖交換端點傳送請求,以交換存取權杖和更新權杖的授權碼。

對於這些要求,grant_type 的值是 authorization_code,而 code 的值則是您先前授予 Google 的授權碼值。以下是要求交換存取權杖和更新權杖授權代碼的範例:

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

client_id=GOOGLE_CLIENT_ID&client_secret=GOOGLE_CLIENT_SECRET&grant_type=authorization_code&code=AUTHORIZATION_CODE&redirect_uri=REDIRECT_URI

如要交換存取權杖與更新權杖的授權碼,您的權杖交換端點會按照下列步驟回應 POST 要求:

  1. 驗證 client_id 是否將要求來源視為授權來源,且 client_secret 與預期值相符。
  2. 確認授權碼是否有效且尚未過期,且要求中指定的用戶端 ID 與與授權碼相關聯的用戶端 ID 相符。
  3. 確認 redirect_uri 參數指定的網址與初始授權要求中使用的值相同。
  4. 如果無法驗證上述所有條件,請傳回包含 {"error": "invalid_grant"} 的 HTTP 400 Bad Request 錯誤。
  5. 否則,請使用授權碼中的使用者 ID 產生重新整理權杖和存取權杖。這些權杖可以是任何字串值,但必須唯一代表使用者和符記的用戶端,且無法猜測。如果是存取權杖,請一併記錄權杖的到期時間 (通常是核發權杖後的一小時)。重新整理權杖不會失效。
  6. 在 HTTPS 回應的主體中傳回下列 JSON 物件:
    {
    "token_type": "Bearer",
    "access_token": "ACCESS_TOKEN",
    "refresh_token": "REFRESH_TOKEN",
    "expires_in": SECONDS_TO_EXPIRATION
    }
    

Google 會儲存存取權杖和使用者的更新權杖,並記錄存取權杖的有效期限。存取權杖到期時,Google 會使用更新權杖從權杖交換端點取得新的存取權杖。

交換存取權杖的更新權杖

存取權杖到期時,Google 會向您的權杖交換端點傳送要求,以為更新存取權杖交換重新整理權杖。

對於這些要求,grant_type 的值為 refresh_token,而 refresh_token 的值則是您先前先前授予 Google 的重新整理權杖值。以下是要求為存取權杖交換重新整理權杖的要求範例:

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

client_id=GOOGLE_CLIENT_ID&client_secret=GOOGLE_CLIENT_SECRET&grant_type=refresh_token&refresh_token=REFRESH_TOKEN

如要交換存取權杖的更新權杖,您的權杖交換端點會執行下列步驟來回應 POST 要求:

  1. 驗證 client_id 會將要求來源視為 Google,且 client_secret 與預期值相符。
  2. 確認重新整理權杖有效,且要求中指定的用戶端 ID 與與更新權杖相關聯的用戶端 ID 相符。
  3. 如果無法驗證上述所有條件,請傳回包含 {"error": "invalid_grant"} 的 HTTP 400 Bad Request 錯誤。
  4. 否則,請使用更新權杖中的使用者 ID 產生存取權杖。這些權杖可以是任何字串值,但必須唯一代表使用者及權杖的用戶端,且不得有理論。如果是存取權杖,請一併記錄權杖的到期時間 (通常是核發權杖後的一小時)。
  5. 在 HTTPS 回應的主體中傳回下列 JSON 物件:
    {
    "token_type": "Bearer","access_token": "ACCESS_TOKEN","expires_in": SECONDS_TO_EXPIRATION
    }

處理使用者資訊要求

userinfo 端點是受 OAuth 2.0 保護的資源,可傳回與已連結使用者相關的版權聲明。您可以自行決定是否要導入和代管 Userinfo 端點,但下列情況除外:

成功從權杖端點擷取存取權杖後,Google 會向您的使用者資訊端點傳送要求,以擷取連結使用者的基本設定檔資訊。

使用者資訊端點要求標頭
Authorization header 熊類型的存取權杖。

例如,如果您的使用者資訊端點位於 https://myservice.example.com/userinfo,則要求可能如下所示:

GET /userinfo HTTP/1.1
Host: myservice.example.com
Authorization: Bearer ACCESS_TOKEN

為您的 Userinfo 端點處理要求的步驟如下:

  1. 從 Authorization 標頭擷取存取權杖,並傳回與存取權杖相關聯的使用者資訊。
  2. 如果存取權杖無效,請使用 WWW-Authenticate 回應標頭傳回 HTTP 401 未經授權錯誤。以下是 userinfo 錯誤回應的範例:
    HTTP/1.1 401 Unauthorized
    WWW-Authenticate: error="invalid_token",
    error_description="The Access Token expired"
    
    如果 401 未授權,或者在連結過程中傳回任何其他失敗的錯誤回應,錯誤都將無法復原,系統擷取之後的權杖將被捨棄,使用者必須重新啟動連結程序。
  3. {
    "sub": "USER_UUID",
    "email": "EMAIL_ADDRESS",
    "given_name": "FIRST_NAME",
    "family_name": "LAST_NAME",
    "name": "FULL_NAME",
    "picture": "PROFILE_PICTURE",
    }
    

    使用者資訊端點回應
    sub 可識別您系統中使用者的專屬 ID。
    email 使用者的電子郵件地址。
    given_name 選用:使用者的名字。
    family_name 選用:使用者的姓氏。
    name 選用:使用者的全名。
    picture 選用:使用者的個人資料相片。