導入 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
    }

Handle userinfo requests

The userinfo endpoint is an OAuth 2.0 protected resource that return claims about the linked user. Implementing and hosting the userinfo endpoint is optional, except for the following use cases:

After the access token has been successfully retrieved from your token endpoint, Google sends a request to your userinfo endpoint to retrieve basic profile information about the linked user.

userinfo endpoint request headers
Authorization header The access token of type Bearer.

For example, if your userinfo endpoint is available at https://myservice.example.com/userinfo, a request might look like the following:

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

For your userinfo endpoint to handle requests, do the following steps:

  1. Extract access token from the Authorization header and return information for the user associated with the access token.
  2. If the access token is invalid, return an HTTP 401 Unauthorized error with using the WWW-Authenticate Response Header. Below is an example of a userinfo error response:
    HTTP/1.1 401 Unauthorized
    WWW-Authenticate: error="invalid_token",
    error_description="The Access Token expired"
    
    If a 401 Unauthorized, or any other unsuccessful error response is returned during the linking process, the error will be non-recoverable, the retrieved token will be discarded and the user will have to initiate the linking process again.
  3. If the access token is valid, return and HTTP 200 response with the following JSON object in the body of the HTTPS response:

    {
    "sub": "USER_UUID",
    "email": "EMAIL_ADDRESS",
    "given_name": "FIRST_NAME",
    "family_name": "LAST_NAME",
    "name": "FULL_NAME",
    "picture": "PROFILE_PICTURE",
    }
    
    If your userinfo endpoint returns an HTTP 200 success response, the retrieved token and claims are registered against the user's Google account.

    userinfo endpoint response
    sub A unique ID that identifies the user in your system.
    email Email address of the user.
    given_name Optional: First name of the user.
    family_name Optional: Last name of the user.
    name Optional: Full name of the user.
    picture Optional: Profile picture of the user.