E2EテストのCypressでFirebase認証を突破する


何をしたいか

E2EテストにCypressを利用するシーンで、ログイン認証にFibrebaseを採用しているシステムをテスト。
具体的にはログイン画面を経由はするものの、ログイン済みの物としてCypressをシステムにアクセスさせる。

イメージ的にはこんな感じ

何を使うか

  • Node
  • Cypress
  • cypress-firebase
  • firebase API
  • firebase-tools-extra

下準備

この記事で掲載するやり方はCypressを実行するコンピュータにもサーバと同じFirebaseAPIを召喚し、テスト用のTOKENを生成。それを使ってFirebase認証をすり抜けるやり方です。
つまり、FirebaseのProjectIDや、Firebaseコンソールから発行するTEST_UIDなどが必要になります。

FIrebaseコンソールで以下の物を取得してください

  • TEST_UID
  • serviceAccount.json

このやり方はセキュリティ的に大丈夫なのか

TEST_UIDなどの漏洩に気をつけていれば特に心配はないです。
と言うのも、システムにバックドアを仕込むでもなく、Firebase公式で、かつ、コンソールからしか取得できない情報が必要なのでそうそう簡単には他者に突破を許しません。
逆に言うと下準備 で生成する2つの情報はソースコードに直接埋め込まない様に気を付ける必要があります。

※CIだったら環境変数に埋め込むなどの対策が必要です。

実装

以下、基本的な手順です。
なお、Cypressの導入手順は割愛します

.gitignore編集

Gitまたはソース管理使ってる人だけ!
忘れがちだけどセキュリティ上やばいので最初に機密情報をGitの対象から外します

以下2行を、 .gitignoreの一番最後に追加してください

 .gitignore
serviceAccount.json
cypress.env.json

npmパッケージのインストール

Firebase自体とそのツール、及びcypress-firebaseと、他環境へアクセスする場合はcross-envが必要です。

コピペ用

npm install --save cypress-firebase cross-env firebase-tools-extra firebase

Firebaseプログジェクト設定

package.jsonと同じ場所に以下2点を配備してください

  • serviceAccount.json
  • cypress.env.json

ちなみに、cypress.env.jsonには下準備で取得したTEST_UIDを記入します

cypress.env.json
{
  "TEST_UID": "************************",
  "FIREBASE_PROJECT_ID": "*********<serviceAccountのproject_id>",
  "FIREBASE_AUTH_JWT": "<上記2点でもし動かなかったらこの項目を追加してみてください>"
}

.fireabsercファイルの作成

またしてもFirebaseの設定ファイルを作成します
名前は .firebaserc 固定です

 .firebaserc
{
  "projects": {
    "develop": "****<serviceAccountのproject_id>"
  }
}

CORSの設定(ホストと違うPCから実行する時だけ)

Cypressはiframeを使用しているため、CORSのチェックで失敗する事があります。
例えば、自分のlocalhostでCypressを実行して、ステージングや本番環境を実行したい時などです。
その場合、 cypress.json に以下の設定を追記する事で回避します

cypress.json
"chromeWebSecurity": false

Cypressのログインテストスクリプトの準備

シンプルなログインテストを書きます。
注意としては、最初にlogoutを記載しておく事です。クライアントがセッションを持っていて1回目はできたのに2回目は動かないなどというトラブルを防いでいます。

login-specs.js
describe('login form test', function() {
    it('init', function() {
        cy.logout()
    })
    it('login', function() {
        cy.login(Cypress.env('TEST_UID')) // こう書いておけばCIに持っていっても万事OK
        cy.visit('https://hogehoge.jp') //ログイン先
    })
})

npm scriptを追加する

この方法を使うとCypress runでは実行できないので以下の手順を実行するスクリプトを作ってあげます

  • FirebaseTestConfigの作成
  • testConfigを使ってcypress run

package.jsonにこれをコピペしましょう

package.json
  "scripts": {
    "test": "npm run build:testConfig && cross-env CYPRESS_baseUrl=https://hogehoge.jp $(npm bin)/cypress run --browser chrome",
    "build:testConfig": "cypress-firebase createTestEnvFile",
    "test:ui": "npm run build:testConfig && cross-env CYPRESS_baseUrl=https://hogehoge.jp $(npm bin)/cypress open"
  },

ポイント1: CYPRESS_baseUrlと先述の cy.visitのURLは揃えてあげてください
ポイント2: --browser chrome はChromeヘッドレスモードを強制するための指定です。していないとElectronで実行されて起こらないはずのバグを誘発したりします(ブラウザが違うので)

実行

適材適所で先ほどのスクリプトを実行してもらえれば良いのですが、各スクリプトには以下の様な意味があります

  • npm run test

    • testConfigを生成して、ヘッドレスChromeでFirebase認証=>Cypressを実行する。
  • npm run test:ui

    • testConfigを生成して、Cypressの画面を表示してからFirebase認証=>Cypress実行を行います。グラフィカルにエビデンスをみたい時とかにどうぞ
  • npm run build:testConfig

    • Firebaseのホストからテスト用のTOKENを取得します。一度うまくいってしまえば基本的にこれ単体で実行することに意味はありません

以上です

設定手順は以上となります。
もしうまく行かない場合は、エラーに表示されている言葉をよく読んでみてください
もし、baseURLが〜〜といっている場合はFirebaseの設定周りがよろしくない事が原因の様です。
Originがなんとかといっている場合は、chromeWebSecurityの設定を変えてみてください。

最後に

上記はlocalhostから自分のPC、またはWEB上のサービスをテストするための手順です。
私は実際にはCircleCIから実行したかったのでさらにシックハックしながら完遂させました。その時のお話はまた別の機会に記載しようかと思います