Firebase AuthenticationとLINEログイン連携する前に読んで欲しいこと

# これは

たまに、LINE API触ってるエンジニアと話していると、Firebase Authenticationと繋いでいる/繋ぎたいと思っている、
という話を聞く。

恐らく、現時点で LINE ログインをしたいようなシチュエーションだと、わりとライトなプロダクトが多くて、そういうプロダクトでは Firebase を使って開発していることが多いのかな。
例えば、LINE Messaging API の柔軟なメッセージの JSON の保存場所を Firestore にして Bot をつくる、とか。

そのときに、LINE ログインのことを考えると思うのだが、実はとにかくユースケースがややこしいし、情報あんまりなくて困るよね、という話。

まぁ都度ちゃんと考えるしかないよね〜くらいに思っていたけど、そういえばLINE API ExpertになったときにFirebaseが絡んだユースケースは俺に任せて！って宣言しちゃってたなぁと思いだして、できるだけ脳内を書き起こしてみることにした。

とにかく、__前提整理が足りずに過剰実装__してしまったり、__不可逆なミス__をする人が出ないように、やり始めるときにでも見てくれれば、という思いで書きます。


# 要約

- LINEのUserIDとFirebase AuthenticationのUserIDを連携して認証するには、カスタム認証が必要
- カスタム認証が今後どこかで必要となるかどうか？は最初に検討しておこう
- 安易にカスタム認証が必要だと決めつけず、必要なシーンを見極めよう

# 詳しく

## LINEのUserIDとFirebase AuthenticationのUserIDを連携して認証するには、カスタム認証が必要

まず前提として、__Firebase Authenticationはどんなものか__というと、


- メール/パスワード、メールリンク、SMS、テンポラリユーザー、その他別サービスでの認証システム(Google、Facebook、Twitter、GitHub、Microsoft、Yahoo、Apple(!))を使ったログインをFirebaseがよしなにやってくれる
- ↑の認証に基づいて、UserIdのDBがFirebase上に構築される
- Userに権限を付与するフィールドがあったり、Firestoreにアクセスするときのセキュリティルールに適用できたり、ブラウザでのセッション管理をFirebaseがよしなにやってくれたりなどなど、連携して威力を発揮するサービスがFirebase上にある
- ブラウザから使えるSDKと、Service Keyが必要になるAdmin権限のSDKがそれぞれ用意されていて車輪の再発明がだいぶカットされる


と、ざっくりこういうありがたみがあるものです。

それで、なるほどなるほどと見ていくとその中に、、、 __LINEログインがオフィシャルにはサポートしていない！__ わけです。

これはどういうことかというと、Firebase SDKで `firebase.auth().signInWithPopup(〜)` みたいなテンションでサインインできるのがウリなのに、それができないわけですね。

ただ、ないと使えない、、、ということではなくて、それ用に、Firebase Authenticationには__「カスタム認証」__という機能があるわけです。

しかし！これは機能がリッチなわけではなくて、自分で実装しつつ連携をしないといけないのです。

具体的には、、、

- サポートされている場合(例えばFacebook)
    - Facebookで連携してログインしたときに、UserIDは自動で一意なものに割り当てらる。さらに、そのFirebaseのUserとFacebookのUserの紐付けは、こちらが何もしなくても、Firebaseが裏でよしなに行われる。これが、SDKを一発叩くだけでやってくれます。私達は結果としてのFirebaseのUserIDを受け取るだけで良い。
- サポートされていない場合(カスタム認証)
    - カスタム認証の方は、「外部のサインイン済のUserIDから、FireabseのUserを引いてくる」という機能がそもそもない（！！！！）。つまり、1回目のLINEログインに成功してFirebaseでUserが作成されたとして、2回目のLINEログイン時に同じUserを引っ張ってこれないので、別のユーザーとして扱われてしまう。

ということに。

うおお、じゃあ絶対に外部連携できないやんけ〜〜！ってなってしまうんですが、ここでFirebase、1つだけ手段を用意してくれています。

それは、__「他の認証ではランダムに生成されてしまうUserIDを、カスタム認証の場合は自由に設定できる」__という機能なんですね。

幸い、「外部の認証システムのUserIDから、紐付くFireabseのUserを引いてくる」という機能はないが、「指定したUserIDのUser引いてくる」という機能はあるんですね。(当たり前だけど)


つまり、

- 一意のFirebase UserID(文字列)を自作する
- その一意のIDを、「外部のUserIDから引けるようにする」

この2つを行うことによって、初めてカスタム認証ができるわけですね。

大変ややこしい！

超具体的に書くと、

__LINEログインして取得したLINEのUserIDが111だったときに、Firebase AuthenticationのUserIDを111にしてやります。そうすると、LINEログインしてLINEのUserIDを取得すると、それがイコールFirebase UserIDになってるので、知ることができますよね？__

こうするしかないわけです。

_（実際には、LINE以外の外部サービスも考慮して冗長にすると プロバイダ x プロバイダでのUserID　というので、 `line:111` というUserIDにする、みたいなことをします）_

もしくは、 LINE UserID ←→ X ←→ Firebase UserID のように、紐付けテーブルを挟んでDBかなんかに保存しておくなんていうやり方もあります。

いずれ、こういう感じでしかカスタム認証ができないという事情があります。

大変ですね。

いつの時代も、レールから外れた瞬間実装が複雑化するというものです。

なお、↑のような実装をもちろんclientでするわけにはいかないので、Auth Serverみたいなものを自分で立てる必要があります。

カスタム認証しなかったらブラウザ上のJavaScriptのSDKだけで完結したのに。押忍。


## カスタム認証が今後どこかで必要となるかどうか？は最初に検討しておこう

はい、では次はこちら。


さっきのカスタム認証の話には続きがありまして。

これ、ちょっと__設計上のリスク__をはらんでいます。

それは、__「他の認証方法もすべてカスタム認証に引っ張られた設計にせざるを得なくなる」__ということです。

先程カスタム認証は、Firebaseで独自のUserIDをつくる、と書きました。

そうすると、例えばFacebookログインで作成されたFirebase UserにLINEも連携するとどうなるでしょう。

FacebookログインでFirebase Userを作成されてしまった時点で、勝手にUserIDが割り振られています。

そうすると、その後LINEのUserIDでFirebaseのUserIDを作成する、ということはできなくなってしまいます。

それを回避するためには、LINEでログインするときには、別の Firebase UserIDとLINE UserIDをどこかで紐つけておく必要が出てしまいます。
(もしくはFacebookのログインも全部自前サーバから認証しにいってカスタム認証を無理やりするとかもあるけどね。。)

最初に紹介したような、UserIDだけでなんとかする、ということができなくなり、どこかのDBに保存する必要が生じます。

そうすると、段々と自前の認証システムみたいになっていきます。

このへんを考えなしにガンガン実装してしまうと、後で大事な大事なユーザーデータをマイグレーションしたり、システムをリファクタリングしないといけない、というややこしいことになってしまいます。

なので、何が最初必要で、その後何が必要になりうるのかを考えておく必要があります。


## 安易にカスタム認証が必要だと決めつけず、必要なシーンを見極めよう

さて、最初に設計が必要だ、となったわけですが、上記のようにカスタム認証は、
導入後不可逆に影響を与えたり、逆に後から導入しようとすると難しいことになってしまったりします。

なので、慎重に選択していきたいところです。

そして特に、LINEのUserIDをFirebaseのUserIDと連携したいシチュエーションなどは、
わざわざ連携する必要がないケースが多々あると思っています。

最初から難しく連携連携、、と考えていく前に、連携が求められるシチュエーションを整理していきましょう。

ここでは一旦、「明らかにカスタム認証によってメリットが得られる」ケースを整理します。


### A. LINEでユーザー登録もログインもしない
まず一番最初に弾きたいのは、「LINEでユーザー登録もログインもしない」というもの。
LINEのUserIDは後付け連携で欲しいわけですね。

これは、認証をLINEに頼らず行うものの、ユーザーにLINEのUserIDを紐つけることで、Messaging APIをつかって通知したい、
みたいなケースですね。よく見ます。

これは、「カスタム認証」はまぁ要らないです。

そもそも、LINEではないプロバイダでかんたんにFirebase Userを作成できるのに、ログイン機能としてつかうわけでもないLINEのためだけにカスタム認証する必要はないですね。

LINEログインのセッションが必要だ！というわけでもないし、せいぜいFirestoreとかにUser Collectionとかつくっておいて、連携時にLINE UserIDを放り込んでおけばよいだけ。

このLINE UserIDが読み出されるのはせいぜいLINEの通知を送るときくらいですね、おそらく。

### B. LINEでユーザー登録はしないが、ログインはする
次に弾くのは、「LINEでユーザー登録はしないが、ログインはする」というもの。

つまり、一回目の認証は必ずLINEではない、となる。電話番号でサクッと認証！でも後でLINE連携してくれたら、LINEでもログインできるようになるよ！というもの。

これは逆に、既にFirebase Userが作られているので、カスタム認証が「できない」。

DBで、Firebase UserIDとLINE UserIDのマッピングテーブルが必要になりますね。。

まぁあんまりうれしくないですね。そんなにLINEでログインさせたいなら、一番最初からLINEログインマストでさせて、むしろLINEログインをマスターにするとかになると楽なんですけどね。

というのが次。

### C. LINEでユーザー登録もする

最後、「LINEでユーザー登録もする」。

ここでは2パターンに分けましょう。

#### ①LINEしか認証に使わない

カスタム認証をするときにややこしいことを考える必要なし！なのでやっとカスタム認証すべき案件です！

ですが、、、

- Fireabse Authenticationをなぜつかうのか？

は考えた方が良いです。
実際、LINEの認証はアプリ/Webやデバイスに問わずできるはずなので、LINEの認証に全部乗っかってしまえば、Firebase Authenticationがないといけないケースは、認証上ないわけなのです。
FirestoreとかにUser Collection作るだけで賄えてしまうかもしれません。

ここから先は設計次第。FirestoreのSecurity Rulesで使えたり、Firebase Analytics(話せば長くなるけどこれは一考の価値あるとおもう)で連携するなど、
アレコレしたくなるのなら！

これくらいシンプルなユーザー設計なら、あとからFirebase Userが欲しくなっても問題ありません。
User Collectionとかに保存してあったLINE UserIDを元に、カスタム認証でFirebase Userを作れば良いだけなので。
慌てなくていいケース。


#### ②LINE以外の認証も行う

ようし、だんだんわけがわからなくなってきましたか？僕はなってきました。

例えばFirebaseでメールアドレス認証するとか、Facebook認証するとかもしちゃうケース。

要はLINEのUserIDが常にあるよ〜ってわけではないし、一番悩ましい。

とにかくビジネス要件のトップダウンをすべて受け入れてごちゃごちゃになることだけは避けなければいけないケース、とも言える。

例えば、カスタム認証をLINEのときだけする、だとすると、Facebookとか他の認証方法でつくられたFirebase UserをLINEと紐つけるときに外部テーブルが必要だし、
カスタム認証でつくられたユーザーにFacebookとか他の認証方法でのログイン情報を紐つけるのは、やったことないからわかんないけど、恐らく外部テーブル必要なのかな。

そう考えたときに、クライアントの認証とセッションを持ったりFirebaseの他の機能と連携できるという意味でFirebase Authenticationのメリットはもちろんある。
あるんだけど、突然開発が膨れるので、そもそも必要要件なのか十分要件なのかは整理しておいて良さそうな事案。

立ち上げとかのタイミングだったら、認証も機能を少し絞っていってもいいかもしれない。

# まとめ

Firebase AuthenticationがAuth系SaaSのすべてじゃないし、ベストでもないし、銀の弾丸がない上でなんかがんばろう。
とにかく、安易にカスタム認証に手を出さないようにしよう、大体のケースでは不要なはなずなんだ！という感じの話でした！

駄文乱文が後半ひどくなって来てるので、困った人が居たら何でも話します、話しましょう。
FirebaseもLINEもやっている稀有な方とはぜひ会いたいです