Google Sign-In for the Assistant で Acount Linking を行う

#Google Sign-In for the Assistant とは
これまでも`Account Linking`を使ってサインインことができましたが、最初設定に難儀するし、トークンでの情報取得など、面倒でもありました。でも、`Google Sign-In`が日本で利用可能になった事で、Google アカウントでのサインインが楽にできるようになりました。
###メリット
1. **ユーザーの Google アカウント情報を利用できる**<br />ボイスマッチを行っているユーザーは必然的に Googleアカウント を持っているので、認証元として一番相性の良いサービス。
2. **oAuth2 サーバーを実装する必要がない**[^Google Sign-In only]<br />Acount Linking は oAuth2クライアント そのもので、なんらかの oAuth2認証フローを実装したサービスが必要ですが、Google Sign-in は アシスタントのためにあらかじめ用意された Googleのサービス に対しての認証方法で、比較的手軽に利用できます
3. **他のプラットフォームのアプリ間でデータを共有できる**
4. **VUIで Acount Linking が完結できる**<br />oAuth2認証フロー にはブラウザを介したステップがあり、通常の Acount Linking はディスプレイがある端末に限定されます。が、Google Sign-in はそれをスキップでき、VUIで完了できます。**シンプルながとても良い**

###デメリット？
1. もちろん、取得できる個人情報は限定的です
2. ユーザーを躊躇させるかもしれない UI（後述：実際のユーザーの操作）

##Google Sign-In for the Assistant の設定
Google Sign In の設定はとてもシンプルです（Google Sign-In only : 別途認証システムを必要としないパターン）
Acount Linking の設定は`Action on Google`のサイドメニューにあります
![kako-dUvCtlb9nbvvSfjn.png](https://qiita-image-store.s3.amazonaws.com/0/70983/8a14c35f-9df4-9d78-1408-9c76a376210e.png)
↑Client information の Client ID をメモっておきます
##Fillfullment
`Fillfullment (Firebase functions)`の方は通常の Acount Linking の作法と同じです。 唯一、`Client ID`を指定する点が他の認証方法と異なります。

    'use strict';
 
    const functions = require('firebase-functions');
    const {dialogflow, SignIn} = require("actions-on-google");

    // Client ID を指定して初期化
    const CLIENT_ID = '[YOUR_CLIENT_ID]' // *1
    const app = dialogflow({
      clientId: CLIENT_ID
    });

    app.intent('Default Welcome Intent', (conv) => {
      conv.ask('やぁ！');
    });

    // アカウントリンクを実施するときに呼び出すIntent
    app.intent('Sign In', (conv) => {
      conv.ask(new SignIn('このサービスの利用を継続するには')) // *2
    });

    // Helper event : actions_intent_SIGN_IN // *3
    app.intent('Get Signin', (conv, params, signin) => {
      if (signin.status === 'OK') {
        const payload = conv.user.profile.payload
        conv.ask(`I got your account details, ${payload.name}. What do you want to do next?`)
      } else {
        conv.ask(`I won't be able to save your data, but what do you want to do next?`)
      }
    })

    exports.dialogflowFirebaseFulfillment = functions.https.onRequest(app);

####1 : Client ID
Acount Linking で取得した Client ID を指定して dialogflow を初期化します
####2 : アカウントリンクフローを開始
要は new SignIn() が呼び出される Intent を作成、会話の任意のタイミングで呼び出します。
####3 : ヘルパーイベント
conv.ask(new SignIn()) でアカウントリンクフローが開始されます。その結果は`ヘルパーイベント`「actions_intent_SIGN_IN」を発火するので、Fulfillmentで受けています。

ヘルパーイベントがちょっとわかりにくいですが、承認や確認などの定型的手続きに一貫した標準のUIを提供するお助け機能です[^Helpers]。SignIn については`actions_intent_SIGN_IN`ヘルパーを使用する、というルールになっていて、例えば Dialogflow の以下のような設定が上記のコードに呼応します。

![GetSignIn.PNG](https://qiita-image-store.s3.amazonaws.com/0/70983/590655e1-ee6a-1380-e5e3-e1f3d36c26fb.png)

##実際のユーザーの操作
conv.ask(new SignIn()) を実行すると...↓
<img src="https://qiita-image-store.s3.amazonaws.com/0/70983/12686a56-43ab-258a-e445-33c9b65b4f35.png" width="320">
「詳細を確認」すると...↓
<img src="https://qiita-image-store.s3.amazonaws.com/0/70983/8d918b1b-2f69-4bc5-4804-878d19bf2382.png" width="320">
「はい」すると...↓
<img src="https://qiita-image-store.s3.amazonaws.com/0/70983/0caee8d0-85e4-8779-ec42-cb9a1adb177d.png" width="320">
「**または**アカウントを作成」って変じゃね？
このあと、actions_intent_SIGN_IN でサインイン後のフォローが遂行されれば成功

ちょっと躊躇するけど、本来、個人情報を取得するならこれくらいの説明義務があるってことだね

##Googleアカウント情報の取得
リンク済のユーザーについて、以下のように参照できます

    app.intent('My Acount', (conv) => {
      let a = [];
      const payload = conv.user.profile.payload
      ...
    }
↑payloadの中身は以下のようなものです↓
（アカウントの状態によって内容は違うかもしれませんが、当然、Google Sign-In for the Assistant に定められたスコープに限定されます）

    {
      "name": "やまもとじゅん",
      "given_name": "じゅん",
      "family_name": "やまもと",
      "email": "**@**", // ユーザーの電子メールアドレス（存在する場合）,
      "email_verified" : true,
      "picture" : "https://**" // プロフィール写真
      "hd" : "**",// Google Apps ユーザーなら、そのドメイン名
      // 以下 JWT 関連（良く知らん）
      "sub": 123456**, // JWT発行者から発行された識別子
      "iss": "https://accounts.google.com", // JWT発行者
      "aud": "CLIENT_ID.apps.googleusercontent.com", // クライアント側の識別子
      "iat": 1536632553,　// JWT発行日時
      "exp": 1536636153,　// 期限切れ日時
      "jti" : , // JWTのための一意な識別子
      "nbf" : , // JWTが有効になる日時
    }
##検証に役立つコトとか
ディスプレイがある端末なら、`[アシスタント名]について`から情報が得られます。

- 一度リンクしたアカウントを解除したりすることもできます。
- `userStorage`に保存された情報も確認できます
- アプリの（状態）リセットもできます（userStorageも初期化されます）  
<img src="https://qiita-image-store.s3.amazonaws.com/0/70983/701398db-2728-335f-ad44-f0821e076dcf.png" width="320">

##その他の参照情報
- Documentation : [
Account linking with Google Sign-In](https://developers.google.com/actions/identity/google-sign-in)

[^Google Sign-In only]: 最小限の構成では別途認証サービスを用意する必要はありません。(Google Sign-In only)。独自の認証サービスと併用し、アカウントをシームレスに管理することも可能です。<br />参考 : [Google Sign-In for the Assistantを試してみた #io18jp](https://qiita.com/flatfisher/items/4278bab47eaa7e8d5971)
[^Helpers]: [Conversations "Helpers"の日本語訳です](https://www.eisbahn.jp/yoichiro/2018/02/actions_on_google_conversations_4.html)