透過 OAuth 式 Google 登入簡化帳戶連結程序

OAuth 式 Google 登入「簡化」連結類型會在 OAuth 帳戶連結之上添加 Google 登入功能。這樣不僅能為 Google 使用者提供流暢的語音連結功能,還能為以非 Google 身分註冊您服務的使用者啟用帳戶連結功能。

此連結類型從「Google 登入」開始,可讓您檢查系統中是否存在使用者的 Google 個人資料資訊。如果在系統中找不到使用者資訊,則會開始標準 OAuth 流程。使用者也可以選擇使用其 Google 個人資料資訊建立新帳戶。

圖 1:動作獲得使用者的 Google 個人資料存取權後,您就能使用動作在驗證系統中尋找相符的使用者。

如要使用簡化連結類型執行帳戶連結作業,請按照下列一般步驟操作:

  1. 首先,請使用者同意存取自己的 Google 個人資料。
  2. 請使用使用者個人資料中的資訊辨識使用者。
  3. 如果在驗證系統中找不到與 Google 使用者相符的結果,根據您在 Actions 控制台中設定 Actions 專案,允許透過語音建立使用者帳戶,或僅能在您的網站上建立使用者帳戶,流程也會隨之繼續進行。
    • 如果您允許透過語音建立帳戶,請驗證從 Google 接收的 ID 權杖。然後,您可以根據 ID 權杖中的個人資訊建立使用者。
    • 如果您不允許透過語音建立帳戶,系統會將使用者轉移至瀏覽器,以便載入您的授權頁面並完成使用者建立流程。
如果您允許透過語音建立帳戶,但在驗證系統中找不到 Google 設定檔,則必須驗證 Google 提供的 ID 權杖。然後,您可以根據 ID 權杖中的個人資訊建立使用者。如果您不允許透過語音建立使用者帳戶,系統會將使用者轉移至瀏覽器,以便載入您的授權頁面並完成流程。
圖 2. 在系統中找不到使用者資訊時的 OAuth 和 Google 登入流程示意圖。

支援透過語音建立帳戶

如果您允許透過語音建立使用者帳戶,Google 助理會詢問使用者是否要執行下列操作:

  • 使用孩子的 Google 帳戶資訊在系統上建立新帳戶,或
  • 如果驗證系統已有 Google 以外的帳戶,請使用其他帳戶登入。

如果想要盡量減少帳戶建立流程的阻礙,建議您允許透過語音建立帳戶。使用者只有在想使用現有非 Google 帳戶登入時,才需要離開語音流程。

不允許透過語音建立帳戶

如果您禁止透過語音建立使用者帳戶,Google 助理會開啟您提供給使用者驗證的網站網址。如果互動是在沒有螢幕的裝置上進行,Google 助理會將使用者導向手機,以便繼續進行帳戶連結流程。

在下列情況下,建議您不允許建立作業:

  • 您不希望擁有非 Google 帳戶的使用者建立新的使用者帳戶,並希望他們改為連結至驗證系統中現有的使用者帳戶。舉例來說,如果你提供會員方案,建議確保使用者不會失去現有帳戶中累積的點數。

  • 您必須能完全掌控帳戶建立流程。例如,如果您需要在建立帳戶時向使用者顯示服務條款,則可以禁止建立。

實作 OAuth 式 Google 登入「簡易」連結

帳戶會與業界標準 OAuth 2.0 流程連結。Actions on Google 支援隱含和授權碼流程。

在隱式程式碼流程中,Google 會在使用者的瀏覽器中開啟您的授權端點。成功登入後,系統會將長期存取權杖傳回 Google。從現在起,每次透過 Google 助理傳送給您動作的要求中,都會包含這個存取權杖。

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

  • 授權端點,該端點負責將登入 UI 提供給未登入的使用者,並以簡碼授權代碼的形式,記錄使用者要求的存取權。
  • 權杖交換端點,負責以下兩種交換類型:
    1. 交換長期更新權杖的授權碼和短期存取權杖。這項交換作業會在使用者完成帳戶連結流程時進行。
    2. 對短期存取權杖交換交換憑證。當 Google 需要新的存取權杖,因為更新權杖已過期時,就會發生此交換行為。

雖然隱含程式碼流程的實作方式較簡單,但 Google 建議使用隱含流程發布的存取權杖不會過期,因為若權杖與隱含流程搭配使用,就會強制使用者重新連結帳戶。如果基於安全考量而需要權杖過期,您應該考慮改用授權碼流程。

設定專案

如要將專案設為使用簡化連結,請按照下列步驟操作:

  1. 開啟動作主控台,然後選取要使用的專案。
  2. 按一下「開發」分頁標籤,然後選擇「帳戶連結」
  3. 啟用「帳戶連結」旁的切換按鈕。
  4. 在「建立帳戶」部分中,選取「是」

  5. 在「連結類型」中,選取「OAuth 與 Google 登入」和「隱含」

  6. 在「客戶資訊」中執行下列操作:

    • 將值指派給「Actions to Google」的用戶端 ID,即可識別來自 Google 的要求。
    • 插入授權和權杖交換端點的網址。
  7. 點按「儲存」

實作 OAuth 伺服器

为了支持 OAuth 2.0 隐式流程,您的服务会通过 HTTPS 提供授权端点。此端点负责验证数据访问并从用户那里获得同意。授权端点会向尚未登录的用户呈现登录界面,并记录用户同意所请求的访问。

当您的 Action 需要调用某项服务的已授权 API 时,Google 会使用此端点从您的用户处获取权限,以便代表他们调用这些 API。

由 Google 发起的典型 OAuth 2.0 隐式流会话具有以下流程:

  1. Google 会在用户的浏览器中打开您的授权端点。用户如果尚未登录,则登录;如果用户尚未授予权限,则授予 Google 使用您的 API 访问其数据的权限。
  2. 您的服务会创建访问令牌,并使用附加到请求的访问令牌将用户的浏览器重定向回 Google,从而将其返回给 Google。
  3. Google 会调用您的服务的 API,并为每个请求附加访问令牌。您的服务会验证访问令牌是否授予 Google 访问 API 的授权,然后完成 API 调用。

处理授权请求

当您的 Action 需要通过 OAuth 2.0 隐式流程执行帐号关联时,Google 会通过包含以下参数的请求将用户发送到您的授权端点:

授权端点参数
client_id 您分配给 Google 的客户端 ID。
redirect_uri 您要将该请求的响应发送到的网址。
state 在重定向 URI 中原封不动地传回 Google 的簿记值。
response_type 要在响应中返回的值的类型。对于 OAuth 2.0 隐式流程,响应类型始终为 token

例如,如果您的授权端点位于 https://myservice.example.com/auth,请求可能如下所示:

GET https://myservice.example.com/auth?client_id=GOOGLE_CLIENT_ID&redirect_uri=REDIRECT_URI&state=STATE_STRING&response_type=token

为了让授权端点能够处理登录请求,请执行以下步骤:

  1. 验证 client_idredirect_uri 值,以防止授予对意外或配置错误的客户端应用的访问权限:

    • 确认 client_id 与您分配给 Google 的客户端 ID 匹配。
    • 确认 redirect_uri 参数指定的网址采用以下格式:
      https://oauth-redirect.googleusercontent.com/r/YOUR_PROJECT_ID
      YOUR_PROJECT_ID 是在 Actions 控制台的 Project settings 页面上找到的 ID。
  2. 检查用户是否登录了您的服务。如果用户未登录,请完成服务的登录或注册流程。

  3. 生成 Google 将用于访问您的 API 的访问令牌。访问令牌可以是任何字符串值,但它必须唯一地代表用户和令牌所面向的客户端,并且必须不可猜测。

  4. 发送 HTTP 响应,将用户浏览器重定向到 redirect_uri 参数指定的网址。在网址片段中添加以下所有参数:

    • access_token:您刚刚生成的访问令牌
    • token_type:字符串 bearer
    • state:原始请求中的未修改状态值 以下是所生成网址的示例:
      https://oauth-redirect.googleusercontent.com/r/YOUR_PROJECT_ID#access_token=ACCESS_TOKEN&token_type=bearer&state=STATE_STRING

Google 的 OAuth 2.0 重定向处理程序将接收访问令牌,并确认 state 值未更改。Google 为您的服务获取访问令牌后,会将该令牌作为 AppRequest 的一部分附加到对您的 Action 的后续调用。

處理自動連結

當使用者同意動作存取自己的 Google 設定檔後,Google 會傳送要求,其中包含已簽署的 Google 使用者身分宣告。斷言包含使用者的 Google 帳戶 ID、名稱和電子郵件地址。專案所設定的權杖交換端點會處理這項要求。

如果驗證系統中已有對應的 Google 帳戶,您的權杖交換端點就會傳回使用者的權杖。如果 Google 帳戶與現有的使用者不相符,權杖交換端點會傳回 user_not_found 錯誤。

要求的格式如下:

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

grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&intent=get&assertion=JWT&consent_code=CONSENT_CODE&scope=SCOPES

權杖交換端點必須能處理下列參數:

權杖端點參數
grant_type 要交換的權杖類型。對於這些要求,這個參數的值為 urn:ietf:params:oauth:grant-type:jwt-bearer
intent 對這些要求而言,這項參數的值為「get」。
assertion 提供已簽署的 Google 使用者身分識別資訊的 JSON Web Token (JWT)。JWT 含有資訊,包括使用者的 Google 帳戶 ID、名稱和電子郵件地址。
consent_code 選用:如果有這個一次性代碼,代表使用者已同意動作存取指定範圍。
scope 選用:任何您設定 Google 向使用者要求的範圍。

權杖交換端點收到連結要求後,應執行以下操作:

Validate and decode the JWT assertion

You can validate and decode the JWT assertion by using a JWT-decoding library for your language. Use Google's public keys (available in JWK or PEM format) to verify the token's signature.

When decoded, the JWT assertion looks like the following example:

{
  "sub": 1234567890,        // The unique ID of the user's Google Account
  "iss": "https://accounts.google.com",        // The assertion's issuer
  "aud": "123-abc.apps.googleusercontent.com", // Your server's client ID
  "iat": 233366400,         // Unix timestamp of the assertion's creation time
  "exp": 233370000,         // Unix timestamp of the assertion's expiration time
  "name": "Jan Jansen",
  "given_name": "Jan",
  "family_name": "Jansen",
  "email": "jan@gmail.com", // If present, the user's email address
  "locale": "en_US"
}

In addition to verifying the token's signature, verify that the assertion's issuer (iss field) is https://accounts.google.com and that the audience (aud field) is the client ID assigned to your Action.

檢查該 Google 帳戶是否已在驗證系統中

確認是否符合下列任一條件:

  • Google 帳戶 ID (位於斷言 sub 欄位) 位於使用者資料庫中。
  • 斷言中的電子郵件地址與使用者資料庫中的使用者相符。

如果其中一項條件為 true,表示使用者已經註冊,您可以發出存取權杖。

如果斷言中指定的 Google 帳戶 ID 和電子郵件地址都與資料庫中的使用者不符,表示使用者尚未註冊。在此情況下,您的憑證交換端點應以指定 error=user_not_found 的 HTTP 401 錯誤回覆,如以下範例所示:

HTTP/1.1 401 Unauthorized
Content-Type: application/json;charset=UTF-8

{
  "error":"user_not_found",
}
Google 收到包含 user_not_found 錯誤的 401 錯誤回應時,會使用設為「建立」的 intent 參數值呼叫權杖交換端點,並傳送包含使用者設定檔資訊的 ID 權杖。

通过 Google 登录功能处理账号创建

当用户需要在您的服务上创建帐号时,Google 会向您的令牌交换端点发出一个指定 intent=create 的请求,如以下示例所示:

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

response_type=token&grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&scope=SCOPES&intent=create&consent_code=CONSENT_CODE&assertion=JWT[&NEW_ACCOUNT_INFO]

assertion 参数包含一个 JSON 网络令牌 (JWT),该令牌提供了 Google 用户身份的已签名断言。JWT 包含用户的 Google 帐号 ID、姓名和电子邮件地址等信息,您可以使用这些信息在您的服务上创建新帐号。

为了响应帐号创建请求,您的令牌交换端点必须执行以下操作:

Validate and decode the JWT assertion

You can validate and decode the JWT assertion by using a JWT-decoding library for your language. Use Google's public keys (available in JWK or PEM format) to verify the token's signature.

When decoded, the JWT assertion looks like the following example:

{
  "sub": 1234567890,        // The unique ID of the user's Google Account
  "iss": "https://accounts.google.com",        // The assertion's issuer
  "aud": "123-abc.apps.googleusercontent.com", // Your server's client ID
  "iat": 233366400,         // Unix timestamp of the assertion's creation time
  "exp": 233370000,         // Unix timestamp of the assertion's expiration time
  "name": "Jan Jansen",
  "given_name": "Jan",
  "family_name": "Jansen",
  "email": "jan@gmail.com", // If present, the user's email address
  "locale": "en_US"
}

In addition to verifying the token's signature, verify that the assertion's issuer (iss field) is https://accounts.google.com and that the audience (aud field) is the client ID assigned to your Action.

验证用户信息并创建新账号

检查是否满足以下任一条件:

  • Google 帐号 ID(可在断言的 sub 字段中找到)位于您的用户数据库中。
  • 断言中的电子邮件地址与用户数据库中的用户匹配。

如果满足上述任一条件,请提示用户通过 HTTP 401 错误响应请求,将 error=linking_error 和用户的电子邮件地址指定为 login_hint,从而将其现有帐号与其 Google 帐号相关联,如以下示例所示:

HTTP/1.1 401 Unauthorized
Content-Type: application/json;charset=UTF-8

{
  "error":"linking_error",
  "login_hint":"foo@bar.com"
}

如果两个条件都不满足,则使用 JWT 中提供的信息创建新的用户帐号。新帐号通常不会设置密码。建议您将 Google 登录功能添加到其他平台,让用户能够在应用的各种途径中通过 Google 登录。或者,您也可以通过电子邮件向用户发送用于启动密码恢复流程的链接,以便用户设置用于在其他平台上登录的密码。

创建完成后,发出访问令牌 ,然后返回 HTTPS 响应正文中 JSON 对象中的值,如以下示例所示:

{
  "token_type": "Bearer",
  "access_token": "ACCESS_TOKEN",
  
  "expires_in": SECONDS_TO_EXPIRATION
}

設計驗證流程的語音使用者介面

檢查使用者是否已通過驗證,並啟動帳戶連結流程

  1. Actions 主控台中開啟 Actions Builder 專案。
  2. 建立新的場景,以便在動作中開始連結帳戶:
    1. 按一下「Scenes」
    2. 按一下「add」圖示 (+) 即可新增場景。
  3. 在新建立的場景中,按一下「Conditions」(條件) 圖示
  4. 新增條件,檢查與對話相關聯的使用者是否為已驗證的使用者。如果檢查失敗,您的動作就無法在對話期間執行帳戶連結,而是應改回提供不需要帳戶連結的功能。
    1. 在「Condition」(條件) 下方的 Enter new expression 欄位中,輸入下列邏輯:user.verificationStatus != "VERIFIED"
    2. 在「轉換」下方,選取不需要連結帳戶的場景,或不需要訪客專屬功能的進入點。

  1. 按一下「條件」的「新增」圖示
  2. 新增條件,在使用者沒有相關聯的身分時觸發帳戶連結流程。
    1. 在「Condition」(條件) 下方的 Enter new expression 欄位中,輸入下列邏輯:user.verificationStatus == "VERIFIED"
    2. 在「轉換」下方,選取「帳戶連結」系統場景。
    3. 點按「儲存」

儲存後,名為 <SceneName>_AccountLinking 的帳戶連結系統場景就會新增至專案中。

自訂帳戶連結情境

  1. 在「場景」下方,選取帳戶連結系統場景。
  2. 按一下「Send 提示」,然後新增簡短句子,說明動作需要存取其身分的原因 (例如「如要儲存偏好設定」)。
  3. 點按「儲存」

  1. 在「條件」下方,按一下「如果使用者成功完成帳戶連結」
  2. 設定使用者同意連結帳戶時,流程的後續步驟。 舉例來說,呼叫 Webhook 以處理任何所需的自訂商業邏輯,然後切換回原始場景。
  3. 點按「儲存」

  1. 在「條件」下方,按一下「如果使用者取消或關閉帳戶連結」
  2. 如果使用者不同意連結帳戶,請設定流程。例如,傳送已確認的訊息,然後重新導向至提供不需要連結帳戶的功能的場景。
  3. 點按「儲存」

  1. 在「條件」下方,按一下「如果發生系統或網路錯誤」
  2. 設定若帳戶連結流程因系統或網路錯誤而無法順利完成時,流程該如何繼續。 例如,傳送已確認的訊息,然後重新導向至提供不需要連結帳戶的功能的場景。
  3. 點按「儲存」

處理資料存取要求

如果 Google 助理要求包含存取權杖,請先檢查存取權杖是否有效且尚未過期,然後從與權杖相關聯的使用者帳戶資料庫中擷取。