最近趣味で作っているWebアプリをHerokuにデプロイしてReview Appsを導入したところ, Firebase Authenticationと相性が悪くてちょっとハマったという話.
当該のアプリではFirebase Authenticationを使っていて, ログインボタンを押すとTwitterのOAuth認証画面へとリダイレクトし, 承認するとアプリへ戻りログイン状態になるというよくあるSNS認証機能がある. Firebase Authenticationには承認済みドメインという概念が存在していて, これにあるドメインを追加するとそのドメインからの認証を許可することができる. 逆に言えば, 追加されてないドメインからは認証を拒否される. 認証可能なドメインをホワイトリストで制限することで, ユーザを不正なドメインから認証させ, アクセストークンを盗むフィッシングから守ったりしてくれている. 当該のアプリは example.herokuapp.com
のようなドメインで配信されているので, それと同じものをFirebase Authenticationの承認済みドメインに登録している.
が, このアプリに対してReview Appsを導入すると問題が発生する. Review AppsはP-Rごとに環境を立ち上げそれらに自動で example-pr-1.herokuapp.com
のようなドメインを割り振ってくれるのだが, 当然これらのドメインは承認済みドメインとして設定されてないため, Firebase Authenticationでログインしようとしても弾かれてしまう. Firebase Authenticationのコンソールを開き, 手動で承認済みドメインに1つずつ登録していくのも手ではあるが, 面倒なのであんまりやりたくない. 困った...
とりあえずパッと思いついた解決策を書き出してみる.
案
- サブドメイン
herokuapp.com
ごと許可してしまうhogehoge.herokuapp.com
のようなドメインからのログインを許可してしまい, 誤ってログインしたユーザがアクセストークンを盗まれる可能性がある
- 正規表現を使い, 承認済みドメイン
example-pr-*.herokuapp.com
を登録する- Auth0では正規表現で承認済みドメインを登録できる
- しかしHerokuでは
example-pr
やexample-10000
をアプリ名とするアプリを作成できてしまうため, 根本的な解決にはならない
pr-*.example.app
のようなドメインを持つプロキシサーバを立て, 認済みドメインにexample.app
を登録する- サーバの用意が面倒なのが難点
- 自動で承認してもらうのを諦め, ログインが必要な機能の検証をしたい時だけ手動でドメインを承認済みドメインに追加する
- コードを一切変更しなくて良い
- ただし承認済みドメインの追加, 及び不要になったドメインの削除を全て手動でやらなければならない
- 開発環境で利用できるログインボタンを追加する
- 開発環境用のFirebase Authインスタンスを新規に作成し, そのインスタンスを使ってログインできるボタンを追加で設置する方法
- 開発用と本番用の認証データを分けるというのがポイント
- 開発用Firebase Authは開発者しか使わないので, 承認済みドメインのスコープがゆるくても問題ない
- 開発者自身が怪しいドメインでのOAuth認証を拒否すれば良い
- 開発環境用のFirebase Authインスタンスの承認済みドメインには
herokuapp.com
を, 本番用にはexample.herokuapp.com
を設定しておく - こうすることでReview Appsで自動生成されるドメインからもログインでき, かつユーザを危険な状態に晒さずに済む
- 開発環境向けログインボタンは
example-pr-*.herokuapp.com
では見えるようにし,example.herokuapp.com
では隠すようにしておけばユーザから触られる心配もない - 一方で開発用と本番用で別々のuidが発行されるので, Review Appsで本番用のユーザデータを使って動作確認できないという問題がある
- これについては 4 の方法と組み合わせてカバーできそう
3は全てを解決してくれるが多少のガッツが必要でだるい. あとあまりメンテナンスするアプリケーションを増やしたくない... 4はめちゃめちゃ素朴だけど, 個人で開発しているのだったらこれでも十分っぽい. 今回扱っているプロジェクトも個人で開発しているものなので4だけでも十分だけど, DependabotでドカドカP-Rが立ち上がったり, e2eテスト導入してみたいという事情があったためシュッと動作確認できる5も採用することにした.
実装してみた感じでは, 手作業不要でサクッと動作確認できるのは最高. が, 今どちらのfirebase authインスタンスに接続しているかを判定できるようログイン時にlocalStorageに接続先の情報を保存する必要があったりして, 持ち込む複雑さや実装の手間の割にメリットが微妙. 5を採用するかどうかは (少なくとも個人開発しているプロジェクトでは) かなりケースバイケースになりそうだなーという感想です.