LINEアカウントと自サービスのユーザ紐付けを実装してみた


はじめに

LINEの公式アカウント機能を使用することで、
ユーザーとのデータ授受のインタフェースとしてLINEを活用することができます。

LINEを活用すると何が嬉しいのかというと、

  • 自サービスからプッシュでユーザーに行動を促せる
  • 画像などファイルデータも扱えるので、アップローダの実装工数を減らせる

など、大きなメリットがあると思っています。

この記事で書きたいこと

LINEをインタフェースとして活用するにあたり、
実装に迷ったポイントがあったため、共有いたします。

(もっといい方法があるよ、などご指摘・ご意見ございましたら是非コメントください)

なお、本内容は先日開催した「Personium Meetup #2」で
LINEでPersoniumにデータを貯める」として発表した内容を修正したものです。

今回取り上げる課題

さて、LINEを入力のインタフェースとして活用する場合に、課題となるのは何があるでしょうか?
私がまっさきにぶち当たったのは「LINEのユーザーと自サービスユーザーの紐付け」の難しさでした。

LINE公式アカウントの仕様(2020年05月現在)

ではLINEの仕様から見ていきましょう。

LINEは下記のように LINE Developersで設定することで、
指定したURLにWEBフックでトークのイベントを送信させることが可能になります。

ドキュメントはこちら → メッセージ(Webhook)を受信する

送信されるイベント形式

公式アカウントに送信されたWebフックでは下記に示されるような形式で送信されます。

{
  "destination": "<公式アカウントのユーザーID>",
  "events": [],     // <Webhookイベントのリスト>
}

「Webhookイベント」は Webhookイベントのタイプ にあるように、様々な種類が存在します。

たとえば、トークにメッセージが送られたときには下記のように、"type": "message"
オブジェクトが events に含まれて、データが送られてきます。
Webhookイベントオブジェクト より一部抜粋)

{
  "destination": "xxxxxxxxxx",
  "events": [
    {
      "replyToken": "0f3779fba3b349968c5d07db31eab56f",
      "type": "message",
      "mode": "active",
      "timestamp": 1462629479859,
      "source": {
        "type": "user",
        "userId": "U4af4980629..."
      },
      "message": {
        "id": "325708",
        "type": "text",
        "text": "Hello, world"
      }
    },
  /* 中略 */
  ]
}

簡単に内容を説明しますと

プロパティ名 意味
replyToken このトークンを付与してLINE APIに送信すると、このイベントへの返信として扱われる
timestamp イベントの発生時刻
source イベントの送信元情報
message typeが message の場合含まれる、メッセージの内容

つまり、 event の sourcemessage を読み解けば、
「誰が何と発言したのか」がわかります。

問題点

ではこれで完成か?というとそうではありません。

送信元ユーザー によれば、送信元のユーザーを示すsource のオブジェクトは、
下記の形式で表されます。

プロパティ名 概要
type String 文字列user (固定)
userId String 送信元ユーザーのID

ここで厄介なのが、「送信元ユーザーのID」です。

ドキュメントによれば、

ユーザーIDの値は、U[0-9a-f]{32}の正規表現にマッチする文字列です。

とあり、

  • Udeadbeefdeadbeefdeadbeefdeadbeef
  • U00112233445566778899aabbccddeeff

みたいな文字列であることがわかります。
各自が検索で設定できる「hogehoge」のようなIDではなく、
LINEで一意になるように振られたIDが送られてくるのです(どういうロジックで振られるのかは不明です)。

上記を踏まえた上での実装案

ここまでで、LINEの発言データがどのように送られてくるのかわかったと思います。
では、それを自サービスでも使用できるように、
LINEのユーザーIDと自サービスのユーザーIDをマッピングする方法を検討していきます。

その①:(NG)ユーザーID直打ち

一番最初に思いつくのがサービスにおける本人のIDをLINEで入力させる方法。

これは他者が勝手に自分のIDを使ってサービスを利用できてしまう問題が生じます。

その②:(NG)自サービスでLINEのIDを打たせる

では逆にLINEのIDを自サービスで打たせたらどうでしょうか?

これも、他人のトーク内容を自サービスに入力させることができてしまう問題が発生します。
というより、そもそもLINEのIDをユーザーは普通知り得ないので実現困難な方法でしょう。

その③:一時キーを発行して紐付ける(無料でやる)

そこで考えたのが、登録時に一時キーを発行して、それを自サービスで
認証しているユーザーに入力させる方法です。

  1. ユーザーは公式アカウントを「ともだち登録」する(⇒フォローイベント発火)
  2. サービスは受信したフォローイベントの送信元ユーザーIDに紐づく一時キーA を発行する
  3. サービスはreplyTokenを使用してユーザーに一時キーA を送信する(1000通/月まで無料!)
  4. ユーザーはサービスを開き(Aliceとして認証中)、一時キーA を入力する
  5. サービスは受信した一時キーAに紐づくLINE上のIDをAliceと紐付け、確定用の 一時キーB を発行する
  6. ユーザーはLINEを開き、一時キーBを入力する
  7. サービスは受信したメッセージイベントに含まれる 一時キーBのLINE IDと、送信元のLINE IDが一致することを確認し、 AliceとLINE上のIDの紐付けを確定する

このようなフローです。

なぜ 4. の後に続くフローが必要かというと、一時キーA を繰り返し試行されると
他者による乗っ取りが成立してしまうためです。

一方で、一時キーA を十分に長いものにすれば問題ないという見方もあるとは思いますが、
そこは人間に入力させる都合上、ユーザビリティとのトレードオフになるのではないでしょうか。
(実サービスに組み込む上では専門家の意見を仰ぐべきだと思います。)

その④:承認用URLを生成する

「その③」の方法では2種類の一時キーを生成するため、紐付けにやや面倒なフローを取ります。
そこで、一時キーBの入力による紐付けの確定ではなく、
承認用URLのクリックによる確定も考えました。

このようなフローであれば、自分のLINEアカウントがどのユーザーIDに紐付けられるかが
ユーザーにとって一目瞭然ですし、クリックするだけで成立するため、面倒さは軽減されます。

しかし、「公式アカウント」側からメッセージを送信するのにはコストがかかるため、
ユーザビリティとコストのトレードオフといったところでしょうか。

実装

今回は「その③」の方法で試しに作ってみました。

ともだち登録

ユーザーはまず公式アカウントの「ともだち登録」から実施していきます。

一時キーA発行

すると、一時キーAが送付されてくるので、

サービスでの入力

それを自サービスの画面(認証済み)で入力してもらいます。

一時キーB発行

一時キーB をLINEで入力するように促されます。

LINEのURLスキームを使用することで、ボタンを押したときにそのテキストが入力された状態でトーク画面を開くことができます。
テキストメッセージを送る 参照)

一時キーB送信

一時キーを送信すると紐付けが確定されます。

紐付け完了

あとはLINEのトークで自サービスを利用できるようになります。

今回のサンプルでは画像を送信し、
ライフログ的に日々の画像を保存できるようにしてみました。

終わりに

今回は自サービスとLINEのアカウントを連携する方法について検討を行いました。
「どのように一時キーを生成するか?」など議論の余地はまだまだありますが、一助となれば幸いです。

また、今回は「Personium」というOSSの上でサービスを構築し、
そこのアカウント連携をサンプルとして実装してみました。

LINEのようなチャットのプラットフォームは、
ライフログを取っていく上ではPDSととても親和性が高く、
個人データの入力インタフェースとして非常に可能性を感じました。

Personiumについては → こちら

Personiumに興味を持っていただけた方、いらっしゃいましたら是非一緒に開発しましょう。

参考URL