Google ログインによるアカウント リンク

アシスタント用 Google ログインは、アカウントのリンクやアカウント作成に関するユーザー エクスペリエンスをシンプルかつ簡単にするもので、ユーザーとデベロッパーのどちらにとっても便利です。アクションは、ユーザーとの会話中に、ユーザーの Google プロフィール(ユーザーの名前、メールアドレス、プロフィール写真など)へのアクセスをリクエストできます。

これらのプロフィール情報をアクション内で使用して、ユーザーに合わせてパーソナライズしたエクスペリエンスを作り出すことができます。他のプラットフォームのアプリからでも、そのアプリが Google ログインを使用していれば、既存ユーザーのアカウントを検索してそのアカウントにリンクしたり、新しいアカウントを作成したり、ユーザーと直接やり取りする経路を確立したりすることができます。

Google ログインを使用してアカウント リンクを行うには、ユーザーの Google プロフィールにアクセスすることについてユーザーに同意を求めます。次に、メールアドレスなどのプロフィール情報を使用して、そのユーザーを既存システム内で識別します。

Google ログインによるアカウント リンクを実装する

以降のセクションの手順に従って、Google ログインによるアカウント リンクをアクションに追加します。

プロジェクトを構成する

プロジェクトで Google ログインによるアカウント リンクを使用するよう構成するには、次の手順に従います。

  1. Actions Console を開き、プロジェクトを選択します。
  2. [Develop](開発)タブをクリックして、[Account linking](アカウント リンク)を選択します。
  3. [アカウントのリンク] の横にあるスイッチを有効にします。
  4. [Account creation] で、[Yes] を選択します。
  5. [Linking type](リンクタイプ)で、[Google Sign In](Google ログイン)を選択します。

  6. [Client Information](クライアント情報)を開き、[Client ID issued by Google to your Actions](Google がアクションに対して発行したクライアント ID)の値をメモします。

  7. [保存] をクリックします。

認証フローの音声ユーザー インターフェースを設計する

本人確認が完了しているかどうか確認してアカウントのリンクのフローを開始する

  1. Actions Console で Actions Builder プロジェクトを開きます。
  2. 新しいシーンを作成し、アクションでアカウントのリンクを開始します。
    1. [Scenes](シーン)をクリックします。
    2. 追加(+)アイコンをクリックして新しいシーンを追加します。
  3. 新しく作成したシーンで、[Conditions] の追加アイコン アイコンをクリックします。
  4. 会話に関連付けられたユーザーが確認済みユーザーかどうかを確認する条件を追加します。チェックが失敗した場合、アクションは会話中にアカウント リンクを実行できないため、アカウント リンクを必要としない機能にアクセスできるようにフォールバックする必要があります。
    1. [Condition] の Enter new expression フィールドに、次のロジックを入力します。 user.verificationStatus != "VERIFIED"
    2. [Transition] で、アカウントのリンクが不要なシーン、またはゲスト専用機能のエントリ ポイントとなるシーンを選択します。

  1. [Conditions] の追加アイコン をクリックします。
  2. ユーザーに ID が関連付けられていない場合に、アカウントのリンクのフローをトリガーする条件を追加します。
    1. [Condition] の Enter new expression フィールドに、次のロジックを入力します。 user.verificationStatus == "VERIFIED"
    2. [移行] で、[アカウントのリンク] システムシーンを選択します。
    3. [保存] をクリックします。

保存後、<SceneName>_AccountLinking という新しいアカウント リンク システムシーンがプロジェクトに追加されます。

アカウントのリンクのシーンをカスタマイズする

  1. [Scenes](シーン)で、アカウント リンクのシステムシーンを選択します。
  2. [プロンプトを送信] をクリックし、アクションが ID にアクセスする必要がある理由をユーザーに説明する短い文を追加します(「設定を保存するため」など)。
  3. [保存] をクリックします。

  1. [条件] で [ユーザーがアカウントのリンクを正常に完了した場合] をクリックします。
  2. ユーザーがアカウントのリンクに同意した場合のフローの動作を設定します。 たとえば、Webhook を呼び出して必要なカスタム ビジネス ロジックを処理し、元のシーンに戻ります。
  3. [保存] をクリックします。

  1. [条件] で、[ユーザーがアカウントのリンクをキャンセルまたは拒否した場合] をクリックします。
  2. ユーザーがアカウントのリンクに同意しなかった場合にフローがどのように進むかを構成します。たとえば、確認応答メッセージを送信し、アカウント リンクを必要としない機能を提供するシーンにリダイレクトします。
  3. [保存] をクリックします。

  1. [条件] で [システムエラーまたはネットワーク エラーが発生した場合] をクリックします。
  2. システムエラーまたはネットワーク エラーが原因でアカウント リンクのフローを完了できない場合のフローの構成。たとえば、確認応答メッセージを送信し、アカウント リンクを必要としない機能を提供するシーンにリダイレクトします。
  3. [保存] をクリックします。

バックエンドのプロフィール情報にアクセスする

ユーザーがアクションに対して自分の Google プロフィールへのアクセスを許可すると、それ以降そのアクションのリクエストが発行されるたびに、ユーザーの Google プロフィール情報を含む Google ID トークンを受け取ります。

ユーザーのプロフィール情報にアクセスするには、まず次の手順に従ってトークンの検証とデコードを行う必要があります。

  1. お使いの言語の JWT デコード ライブラリを使用してトークンをデコードし、Google の公開鍵(JWK 形式または PEM 形式で使用可能)を使用してトークンの署名を検証します。
  2. トークンの発行元(デコードされたトークンの iss フィールド)が https://accounts.google.com で、オーディエンス(デコードされたトークンの aud フィールド)が Google がアクションに対して発行したクライアント ID の値(Actions Console でプロジェクトに割り当てられている ID)の値であることを確認します。

デコードされたトークンの例を以下に示します。

{
  "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 フルフィルメント ライブラリを使用すると、トークンの検証とデコードが自動的に行われ、プロフィールのコンテンツにアクセスできるようになります(以下のコード スニペットを参照)。

...
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!');
});