LoginSignup
19
14

More than 5 years have passed since last update.

Google Assistantアプリでユーザー識別情報を取得したい

Last updated at Posted at 2018-01-27

はじめに

はじめまして。

動機

Google Home用Google Assistant用のアプリを開発していて、誰が話しているのかを特定したくなりました。
例えばIoTデバイスを操作しようと思った場合、開発環境内だけで個人的に遊ぶアプリなら良いですけど、開発したアプリを公開するためには、何らかのユーザー識別情報が必要です。
ということを考えると、Google Homeを使うにあたってGoogleアカウント情報を取得出来たら便利です。
……便利なのです、が、まだ上手くいっておりません。
なのでここにあるのは、現在私が躓いているポイントを記したメモになります。

最初に試したこと。

予めAWS LambdaとかでWebAPIを用意しておき、DialogflowからWebhookでそのWebAPIにアクセスするとします。その時WebAPIに送信されるJSONデータの中にuserという項目が入っています。1
こんな感じ:

{ ..., 
  user: {
    "lastSeen":"2018-01-01T01:23:45Z",
    "locale":"ja-JP",
    "userId:"hogehoge"
  },
  ...
}

最初はこのuserIdでユーザーを識別できるじゃないかと気楽に考えたのですが、そう簡単には行きません。
このuserId、シミュレーター使った時とGoogle Homeデバイスから話しかけた時とで値が変わったりするのです。

Google公式によるuser IDの説明

まずは素直に2Googleの公式説明を読みます。
https://developers.google.com/actions/identity/user-info

大まかに言って、「匿名のuserID」と、「ユーザーに名前を要求する方法」があるよ、とのこと。
まず「匿名のuserID」の詳細を見ると下記のように分類されています。。

  • Voice-activated speakers
    • When a voice matches
    • When no voice matches
  • Android mobile devices
  • Actions Console Simulator
    • Phone Surface
    • Speaker Surface

続いてLanguagesとUser ID lifetimeの項目がありますが、これは共通説明のようですね。

  • Languages ― UserIDは言語を跨っても同一です。
  • User ID lifetime ― 30日間使われなかったり、ユーザーがデバイスとアカウントを切り離したりすると、UserIDはリセットされます。

で、最初の疑問に対する答えですが、Actions Console Simulator の項目を読むと、ちゃんと書いてありますね。スピーカー使うと別のIDになるようです。
それは解りました。
でも重要なのは、アプリ公開後にユーザー識別できるかどうかです。メインで考えているのはGoogle Homeスピーカーによる操作ですので、Voice-activated speakersのところをよく読みます。
すると…

  • When a voice matches: デバイスに登録済みの声とマッチした場合、不変のuserIDが返る。登録されている音声プロファイルごとに、別々のUserIDになる。このUserIDは、そのデバイスで行われるあらゆる会話に関連づけられる。
  • When no voice matches: デバイスに声を登録していなかったり、誰の声か分からなかった場合、異なるIDになる。このIDはその会話においてのみユニークである。

つまり、Voice Matchを使わない場合、会話セッションが変わっても同じIDが使われるかどうかは保証されないということですね。
ということは、会話セッションをまたいでも変らないUserIDを使いたい場合、Voice Match機能で声を登録するしかない、ということです。
では、Voice Matchを使わない環境ではどうしましょう?
何しろGoogle HomeのVoice Matchを登録できるユーザーは最大6人3という制限がありますし、使用間隔が30日以上空いてしまうことが想定されるアプリでも問題になります。

OK Google, Voice Matchを使わずにユーザーを識別する方法を教えて?

Google Home自体を識別する情報を探してみたけど見つからないので、Googleアカウントを使うことを考えます。
Google Homeを使える環境になっている以上、Googleアカウントは紐づけられているはず。
というわけでたどり着いたのがこちらの記事。

Google assistant でGoogleのアカウントの情報を取得する

これで万事解決……とはなりません。
実際にやってみると、少なくとも3つの課題に直面しました。

課題1 Authorization URLにGoogleのURLは使えない。

まずはAuthorization URL。
手順に従って https://accounts.google.com/o/oauth2/v2/auth と入力してしまうと、Googleのポリシー違反で怒られてしまい、ボタンが押せず、次のステップに進めません。
Googleではなく自分のサービスでOAuthの認可サーバーを作れとのこと。Googleのポリシーが変わったのだから仕方がありません。4

ただ、StackOverflowとかで探してみると、適当なURLを入力して一旦保存し、あとから編集すれば良いとのこと。やってみると、本当にGoogleの認可サーバーを入力できました。わーい。
……。
いやいやダメでしょう。これ間違いなく公開時の審査でひっかかりますって。
横着せずにOAuth 2.0の認可サーバーを立てなきゃダメってことですね。5
でも、Google Driveにアクセスするようなアプリの場合、どうやって解決しているのでしょう?

課題2 ユーザー認証のためにシミュレーターのDEBUG欄を開くの?

2つ目の問題は、認可の方法です。
読み進めていくと、シミュレーターのDEBUGタブに書かれているURLをコピペしてブラウザでアクセスするという手順が出現します。
当然「そんな手順、どうやってユーザーに実行させるの?」というぎもんが出てきますよね。
そんな時はGoogleアシスタントアプリで試してみましょう。するとこんな感じになります。

ユーザー「OK Google, xxxアプリにつないで」
Google「XXXアプリがまだリンクされていません。」
Google「XXXアプリをGoogleアカウントにリンク」

最後のはボタンです。このボタンを押すと、Googleアカウントのログインページ(あるいはアカウント選択ページ)が表示されますので、Googleアカウント(使用中のGoogleアシスタントとは別のアカウントでも良い)でログインして、ID連携を許可することができます。

これを実行した後なら、Google Homeからでも当該アプリを使えるようになります。
上手くいかないときは、Google Homeから、Google Homeアプリで何か設定しろと言われるますので、設定します。(TODO:確認したら追記)

ただしこの手順はまだユーザーにとって手間なので、Google Homeデバイスだけでメールアドレスを取得したいところです。というか、予想はつくと思いますが、この手順で取得できるのはGoogleHomeを使っているユーザーのGoogleアカウントではなく、連携した OAuth2.0サーバーになってしまうんですよね。
課題1で述べた通り、Googleのポリシーにより、認可サーバーを使えない問題もありまして……。
楽天レシピのように、音声だけで「メールアドレスを利用して良いですか?」と確認出来たらとても便利なのですけど。6

課題3 認証が保持される時間

さらに、数時間してから試してみたところと、

「xxxアプリがまだリンクされていません」

と言われまいました。
Implicit flowではなく、Authorization Code flowを選んでも同じです。
やはり、Googleアカウントを使っているのが悪いのでしょうか?
Google Homeスピーカーだけでは認証できないので、このままだと毎回googleアシスタントを立ち上げてもらう必要が出てきます。困った困った。

注文した覚えのないidToken

ちなみに、上述の内容を試してみると、userIdのところは下記のような感じのデータになります。

{ ..., 
  user: {
    "lastSeen":"2018-01-01T01:23:45Z",
    "idToken":"超長い文字列",
    "accessToken":"それなりに長い文字列",
    "locale":"ja-JP",
    "userId:"hogehoge"
  },
  ...
}

"accessToken"を使って、参照した解説記事の通りにgoogleのAPIを叩いてみると、無事にemail情報の入ったJSONが取得できました。

accessTokenを使って取得したJSONデータ
{
 "id": "*********************",
 "email": "b***@gmail.com",
 "verified_email": true,
 "name": "",
 "given_name": "",
 "family_name": "",
 "picture": "https://****/****.jpg",
 "hd": "******************"
}

ちなみにこの b***@gmail.com というのは当然ですが、OAuthの際に入力したほうのGoogleアカウント情報です。

それは良いのですが、ついでに何故か"idToken"も追加されています。いろいろ調べてみた末に、Base64でデコードしてみたところ、OpenID ConnectのidTokenのことだったようで、中身は下記のような感じでした。

idTokenのペイロード
{ 
  azp:"************.apps.googleusercontent.com",
  sub:"*********************",
  email:"a***@gmail.com",
  evail_verified:true,
  exp:**********",
  iss:"https://accounts.google.com",
  jti:"***************************",
  iat:"**********",
  nbf:"**********"
}

「ははーん、なるほどGoogleの OAuthサーバーに対してaccessTokenだけじゃなくてidTokenも取得してくれたんだな、このお人よしめ」と期待しますが、emailの項目がおかしい。
accessTokenで取得したのは b***@gmail.com でしたが、こちらは a***@gmail.com です。
これは Actions on Googleの開発に使っているアカウント、つまりテストに使ったGoogleアシスタントおよびGoogle HomeスピーカーのGoogleアカウントなんですよね。

え、なにこれ。つまりどういうことなんです?
確かに欲しかったのはそのメールアドレスなんですけど、どうしてその情報を送るのにOAuthが必要なんですか?

(追記 2018/1/28)
Dialogflow側で、"Signe in required"のチェックをオフにすると、当然accessTokenは取得されなくなります。
…が、idTokenは送られてきます。
新しくAgentを作って試してみてもidTokenが送信されることはないので、Googleさんが仕様変更したわけでもなさそう。
どういう条件でidToken送ってくるんですか?

まとめ

謎はさらに深まった。


  1. (2018/1/28追記)この情報を確認するだけなら、AWS Lambdaで受け取ったログを確認するまでもなく、Actions on GoogleのシミュレーターのDEBUGタブ見た方が早いですね。 

  2. といってもここにたどり着くまでにいろいろさまよったのですが。 

  3. 1 台の Google Home で Voice Match を使用できるユーザーは、最大 6 人です。 

  4. なんでそんなポリシー変更したんですかGoogle先生?どこかに理由を解説しているドキュメントは無いんですか? 

  5. それかTwitterとかMSとかのOAuthサーバーを使う手もありそうなんですが、ユーザーが折角Googleアカウントにログインしているというのに、他のサービスのアカウントと連携させるというのもちょっとどうかと…) 

  6. たぶんそれを実現する方法がStreamlined Identity flowなのでしょうか。 

19
14
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
19
14