使用 Google 登入功能連結帳戶

使用者和開發人員的 Google 登入提供最簡單輕鬆的使用者體驗,包括連結帳戶及建立帳戶。您的動作可在對話期間要求存取使用者的 Google 個人資料,包括使用者名稱、電子郵件地址和個人資料相片。

您可以使用這類設定檔資訊,在動作中打造個人化的使用者體驗。如果您的應用程式在其他平台上有應用程式,而且這些平台都使用 Google 登入功能,您也可以找出現有使用者帳戶並建立連結,然後建立新帳戶,然後建立與使用者直接溝通的管道。

如要透過 Google 登入功能執行帳戶連結作業,請先要求使用者同意存取其 Google 個人資料。然後,您可以使用其設定檔中的資訊 (例如電子郵件地址) 識別系統中的使用者。

實作 Google 登入帳戶連結

請按照下列各節的步驟,將 Google 登入帳戶連結新增至您的動作。

設定專案

如要將專案設為使用 Google 登入帳戶連結功能,請按照下列步驟操作:

  1. 開啟「Actions Console」並選取專案。
  2. 按一下「開發」分頁標籤,然後選擇「帳戶連結」
  3. 啟用「帳戶連結」旁的切換鈕。
  4. 在「建立帳戶」部分中,選取「是」
  5. 在「連結類型」中,選取「Google 登入」

  6. 開啟「客戶資訊」,並記下「Google 核發給您的用戶端 ID」的值。

  7. 點按「儲存」

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

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

  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 設定檔後,您會在每個後續動作要求中收到一個 Google ID 權杖,其中包含使用者的 Google 個人資訊。

如要存取使用者的個人資料,您必須先按照下列步驟驗證並解碼權杖:

  1. 針對您的語言使用 JWT 解碼程式庫來解碼權杖,並使用 JWKPEM 格式的 Google 公開金鑰 (提供 JWKPEM 格式) 來驗證權杖的簽名。
  2. 確認權杖的核發者 (已解碼權杖中的 iss 欄位) 為 https://accounts.google.com,且目標對象 (已解碼權杖中的 aud 欄位) 是 Google 核發至您動作的用戶端 ID 值,該值會在 Actions 控制台中指派給您的專案。

以下是已解碼權杖的範例:

{
  "sub": 1234567890,        // The unique ID of the user's Google Account
  "iss": "https://accounts.google.com",        // The token's issuer
  "aud": "123-abc.apps.googleusercontent.com", // Client ID assigned to your Actions project
  "iat": 233366400,         // Unix timestamp of the token's creation time
  "exp": 233370000,         // Unix timestamp of the token'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"
}

如果使用 Node.js 適用的 Actions on Google Fulfillment 程式庫,該程式庫會為您驗證權杖並解碼,然後讓您存取設定檔內容,如以下程式碼片段所示。

...
const app = conversation({
  // REPLACE THE PLACEHOLDER WITH THE CLIENT_ID OF YOUR ACTIONS PROJECT
  clientId: CLIENT_ID,
});
...
// Invoked on successful completion of account linking flow, check if we need to
// create a Firebase user.
app.handle('linkAccount', async conv => {
  let payload = conv.headers.authorization;
  if (payload) {
  // Get UID for Firebase auth user using the email of the user
    const email = payload.email;
    if (!conv.user.params.uid && email) {
      try {
        conv.user.params.uid = (await auth.getUserByEmail(email)).uid;
      } catch (e) {
        if (e.code !== 'auth/user-not-found') {
          throw e;
        }
        // If the user is not found, create a new Firebase auth user
        // using the email obtained from Google Assistant
        conv.user.params.uid = (await auth.createUser({email})).uid;
      }
    }
  }
});

處理資料存取要求

如要處理資料存取要求,只要驗證由 Google ID 權杖宣告的使用者已存在於資料庫中即可。下列程式碼片段示範如何檢查使用者訂單是否已存在於 Firestore 資料庫中:

...
app.handle('Place_Order', async conv => {
  const order = conv.session.params.order;
  const userDoc = dbs.user.doc(conv.user.params.uid);
  const orderHistory = userDoc.collection("orderHistory");
  if (orderHistory) {
    // Order history exists, so the user already placed an order.
    // Update counter for order type.
    await orderHistory.doc(order).update({ count: admin.firestore.FieldValue.increment(1)});
  } else {
    // First order they place
    await orderHistory.doc(order).set({ option: order, count: 1});
    options.forEach(opt => {
      if (opt != order) {
        orderHistory.doc(opt).set({ option: opt, count: 0});
      }
    });
  }
  return conv.add(`Your ${order} has been placed. ` +
      'Thanks for using Boba Bonanza, see you soon!');
});