見出し画像

FirebaseでLINE連携をする

今回のテックブログは、複数サービスとの連携機能の実装についてのお話です。公式にサポートされていないサービス連携をどうクリアするか?そのハマりどころから解決までの紆余曲折をお届けします。

はじめに

こんにちは。情報システム部の合間です。
以前、Web開発を担当していたころに振り回された、Firebaseでの他プロバイダ(LINE)連携について、つらつら書いてみます。

いきなり結論

FirebaseでLINE認証・他プロバイダ連携を扱うのは、だいぶ面倒
 → FirebaseでLINE認証を使用することを推奨するかどうかと聞かれたら推奨しない

それでも使いたい
 → 下記のissueを参考にすれば実現できる
 → 他プロバイダと連携するには カスタムプロバイダ認証、および、FirebaseAuthユーザが保持するcustom claimsプロパティを使用するのがミソ

オススメしない理由

1. 開発工数が増える
2. 標準プロバイダのお手軽さと比較すると虚しくなる
3. Firebaseのみで完結しない → メンテナンスの手間が増える
 3-1. 認証用のバックエンドサーバ(Lambdaなど)をおいたり、LINE用の新規登録・バックエンドキック画面をフロント側で作成したりする必要が出るため
 3-2. (そう頻繁ではないだろうが)LINE側のAPIに変更が発生するたびに、自前で用意したバックエンドサーバの手入れが必要となる
 3-3. LINE-APIv2.0サーバ通信用のクレデンシャル情報の管理などの手間が増える

お約束

以下で記述する方法はあくまで一例です。お手柔らかに。

概要

以前、私が携わっていた案件で「とあるWebサービスに、Firebaseを用いて会員機能を実装する」といった要件がありました。
その案件では、会員登録をするにあたって、以下のような要件がありました。

★下記のアカウント登録・連携機能がほしい
 ・Emailアドレス
 ・Google
 ・Facebook
 ・Twitter
 ・LINE

Firebaseに関する知識が皆無だった私は、実現可能性の検討をつけるため、ひとまずドキュメントをあさってみました。すると、Firebaseでは新規登録・アカウント連携などを実現する機能が標準で用意されていました。

「普通だな〜」

と、このときは思っていました。

「メアド、Google、Facebook、Twitterのプロバイダ認証機能は用意されているな〜。ん、LINEは?」

不穏な空気が流れ始める。

ドキュメントを探っても、LINEでのプロバイダ認証については一切記述されていません。
「たまたま見つけてられてなくて、なにか方法はあるでしょ〜」と思いつつ調べたところ、以下記事が見当たりました。

>LINEログインがオフィシャルにはサポートしていない!

「マジか」

LINEは日本でほぼデファクトとなった連絡ツールです。しかし、海を渡った先ではそうでもないことを、こんなところで実感することになりました。
読み進めてみると、LINE専用の認証メソッドは用意されていないものの「カスタムプロバイダ認証」とやらを使えば、LINEアカウントでの「認証」が実現できそうなことがわかりました。

しかし、読み進めると以下のような記述がありました。

>②LINE以外の認証も行う
>例えばFirebaseでメールアドレス認証するとか、Facebook認証するとかもしちゃうケース。
[Firebase AuthenticationとLINEログイン連携する前に読んで欲しいこと]

「今回のケースじゃん」

記事を読む限り、何かしらの外部テーブルを持つ必要が出てきそうなことだったり、そもそも、ベストプラクティス的な開発実績が見当たらなかったり、開発工数がだいぶ膨らみそうだなあ

と、言うことから

ビジネスチームに「今回LINE認証を要件から外すことはできないですか〜?」と相談してみたところ
「あるサイトで、ソーシャルプロバイダを用いた新規登録ではLINEが圧倒的に数が多いと書かれた記事があり、できる限り欲しい」とのこと。その記事の根拠は乏しいものの、否定する資料もないため、実装の方針は変わらず。

とはいえ

「LINE(カスタムプロバイダ)アカウントでFirebaseの認証基盤に登録する方法は見つかった。しかし、メアドや公式プロバイダにアカウントを『連携する』方法は無いのか…」

といった疑問は引き続きありました。


そのとき、当時のチームリーダーから以下のissueを受け取りました。

まさに、今回のケースに合致する内容で、ご丁寧にもLINEプロバイダが想定されていました。
内容としては、custom claims と言ったプロパティを使用し、そこにLINEのユーザIDをFirebaseAuthUserに格納してアカウントを紐付けてあげるといったものでした。

ケースとしては

1. 標準プロバイダにLINEプロバイダを紐付け
2. LINEプロバイダに標準プロバイダを紐付け

と、2パターンの実装が必要とのことでした。

これができれば残るは、

・メアド、標準プロバイダ、カスタムプロバイダ(LINE)で
 1. 新規登録
 2. ログイン
 3. 連携・解除機能

のフローを実装すれば良さそう、と方針が固まりました。

※ メアド、標準プロバイダ、LINE(カスタムプロバイダ)でのサインイン・アップする方法は他サイトの記事などで取り上げられているので、今回は説明を割愛します。大まかな流れは下図になります。
※ 着目すべきは、Google・Facebook・Twitterの簡潔さです。構成表現が棒線一本で済みます。

画像1


連携機能の実装方針

以下に記述している内容を、PythonでLambda上に実装しました。

【紐付け処理】

「標準プロバイダにLINEプロバイダを紐付け」(バックエンド側で紐付ける)
1. 標準プロバイダでログイン済み
2. LINEログインのダイアログを出し、ログイン処理を行いユーザ情報を取得
3. 取得したLINEユーザのメールアドレスで空のFirebaseAuthユーザ(★)を作成
4. 標準プロバイダ側のcustom claimsにLINEのユーザIDをセット
5. LINEプロバイダ側のcustom claimsに標準プロバイダのユーザIDをセット
※ githubのissueには .linkWithCredential() を用いるとあったが、諸問題で使用できなかった。しかし、こちらを使用したほうが、てっとり早いと思われる
「LINEプロバイダに標準プロバイダを紐付け」(フロント側で紐付ける)
1. LINEプロバイダでログイン済み
2. 例えば、Facebookアカウントで紐付けの場合
3. auth.currentUser.linkWithPopup(facebookProvider) で紐付け
[JavaScript を使用してアカウントに複数の認証プロバイダをリンクする]

【連携アカウントでのログイン処理】

「標準プロバイダで新規登録・LINEプロバイダを紐付けし、LINEプロバイダでログインするケース」
1. LINEプロバイダでログインを試行した際に、custom claimsの値を確認
2. 存在する場合、取得したclaimsから標準プロバイダでログインする
 (★のFirebaseAuthユーザに格納されている)
3. 存在しない場合、通常のログイン処理
「LINEアカウントで新規登録・標準プロバイダを紐付けし、標準プロバイダでログインするケース」
1. 何も考えず、標準プロバイダでのログインを行う
 → 「LINEプロバイダに標準プロバイダを紐付け」の操作によって
    Firebase側でアカウントの紐付けが行われているため

【連携解除処理】

・LINEプロバイダ→標準プロバイダのケース
 ・user.unlink(providerId)メソッドを使用し、連携を解除する
  [ユーザー アカウントから認証プロバイダのリンクを解除する]
・標準プロバイダ→LINEプロバイダのケース
 ・custom claimsの内容を空にする
 ・作成したauthユーザ(★)を削除する

…と、文章で読んでもナンノコッチャかと思うので、以下は特にわかりにくい「標準プロバイダをカスタムプロバイダに連携する際のフロー図」になります。

画像2

画像3

一通り実装してみて

・FirebaseでLINEや独自性の高いプロバイダ認証を用いることはオススメできない
・選択可能であれば、他の認証サービスを用いたほうが実装工数は少なく済みそう
・「Googleアカウントを用いた会員登録のみを利用する」といった要件が固まっているのであれば、Firebaseは手軽で良いかも

***

(余談)ハマりどころ?

その1:Trusted/Untrusted Provider問題

今回のケースでは、Firebaseプロジェクトにおいて「重複するメールアドレスでのユーザ登録を許可」しました。

許可した背景
重複メールアドレスを許可しない場合、本来は

 1. Facebookプロバイダで新規登録
 2. その後Googleプロバイダで新規登録をする
 3. 本来は重複登録で弾かれる

といったフローが想定されますが、3. の箇所でFacebookプロバイダでの登録情報が抹消され、Googleプロバイダで登録したような状態となる(下図参照)ためです。

画像4

これには、Truested/Untrusted Providerといった概念があるようで、FirebaseAuthユーザのプロパティである「EmailVerified」の項目をFirebase/adminSDKで「True」にすることで解決できるといった話を見かけました。
試したところ、たしかにFacebookプロバイダは生き残るのですが、Googleプロバイダで新規登録をすると、アカウント連携状態となってしまうため、根本的な解決とはなりませんでした。

※ 「Googleプロバイダが連携しても問題ない」といった条件が許容できるのであれば、メールアドレスの重複登録を許可しない、といった設定で問題ないです。

その2:Firebaseメール送信時の文面

※ 今回の記事内容と全く関係ないですが、Firebase独特な仕様について、調べてもこれといった記述が見当たらなかったので供養としてあげておきます

フロント側で、メールアドレスで会員登録をしたユーザが

・パスワード変更
・パスワードを忘れたとき
・メアド変更時

をしたくなったとき、Firebaseには専用のメソッドが用意されています。そのメソッドを踏むことで、メールがユーザへ送信され各々の機能を実現できます。
しかし、その際に送られるメール文面は、Firebaseコンソール側にあらかじめ設定されているのですが、これを変更しようとする場合、以下のような手順を踏む必要があります

1. フロント側で行われる処理をLambdaを呼び出し、adminSDKで実行
2. 自前で立てたSMTPサーバ(Amazon SESなど)を経由し送信
 ・その際に、メール本文を自由に調整し、セットする

試したところ「パスワード変更」「パスワード忘れ」のメール本文の変更は実現できました。ただし「メアド変更時」ついては実現ができませんでした。その理由は以下のためです。

  ・フロント側でメアドを変更した際には以下メールが同時に送信される
 1. 本人(新しいメアド)に確認のメール
 2. 古いメアドに救済用のメール
  → 救済用の専用リンクを発行する関数がadminSDKに無い

# フロント側のメアド変更:firebase.auth().currentUser.updateEmail()
# バックエンド側でのメアド変更:
 1. admin.auth().updateUser()(メアド変更)
 2. admin.auth().generateEmailVerificationLink()(本人確認メール)
 3. ?(救済用の専用リンクを発行する関数が無さそう)

これについては、我々の間では対応を諦めて、メアド変更時に限り、Firebaseの標準メール文面を使用することとしました。

***

おわりに

Firebaseが公式にLINE認証を対応してくれると、状況は大きく変わるのでないかと思います。

というわけで、

Googleさん、LINE認証をFirebaseで公式に対応してください。