⚙️

LAN内の別ホストからfirebaseエミュレータに繋ぐ

に公開

概要

Vue+Firebase で Web アプリ勉強中です。
LAN 内で別ホスト(モバイル端末)から firebase エミュレータに繋いで動かしてみたので設定周りを簡単レポート。

接続構成

機器の接続構成は下図の通り。
WiFi ルータの Private Net に WinPC と Android 端末と iOS 端末がある。アプリは各端末の Chrome ブラウザで動く Vue アプリで、それぞれを Vite サーバ と Firebase エミュレータに接続して動かしたい。Firebare にも hosting エミュレータがあるけど build した環境でしか動かせないので、Web サーバは Vite の dev 環境を使う。
Vite と firebase は WSL で動かしてるけど、好みの問題なので普通に Windows で動かしてもよいです。

設定

Vite サーバの設定

デフォルトでは別ホストからは繋げないので、server 設定を追加。
試行錯誤の経緯でポート番号を変えてるけど、空いてれば何でもよいです。

vite.config.js
import { fileURLToPath, URL } from "node:url";
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import VueDevTools from "vite-plugin-vue-devtools";

// https://vite.dev/config/
export default defineConfig({
  plugins: [vue(), VueDevTools()],
  resolve: {
    alias: {
      "@": fileURLToPath(new URL("./src", import.meta.url)),
    },
  },
  server: {
    host: true,   // true にするとLAN内の他のホストから接続できる
    port: 4000,   // ポート番号をデフォルトから変えたければ設定
  },
});

firebase emulator の設定

以下のように設定した。
emulators の設定に動かしたいサービスのそれぞれに"host": "0.0.0.0" を追記。
公式ドキュメントでは 「Hosting エミュレータを使用する」[1]のところにだけ書かれてるけど、他のエミュレータも同じと見做して全部書いてみた。
hosting は結果的に使わないけど、一応設定しておく。ついでに各々のポート番号も指定する。
今回は realtime databse を使っていて、セキュリティルールを databse.rules.json に書いている。この設定も database エミュレータが見てくれる。

firebase.json
{
  "hosting": {
    "public": "dist",
    "ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
    "rewrites": [
      {
        "source": "**",
        "destination": "/index.html"
      }
    ]
  },
  "database": {
    "rules": "database.rules.json"
  },
  "emulators": {
    "singleProjectMode": true,
    "hosting": {
      "host": "0.0.0.0",
      "port": 5000
    },
    "ui": {
      "enabled": true,
      "host": "0.0.0.0",
      "port": 5100
    },
    "auth": {
      "host": "0.0.0.0",
      "port": 9099
    },
    "database": {
      "host": "0.0.0.0",
      "port": 9000
    }
  }
}

Windows firewall の設定

Windows の firewall 設定でプライベートネットの必要なポートを全部開けておく。
環境が自働で設定してくれる場合もあります。

PowerShell(管理者権限)
> netsh advfirewall firewall add rule name="Vite server" dir=in action=allow protocol=TCP profile=private localport=4000
> netsh advfirewall firewall add rule name="firebase emulators" dir=in action=allow protocol=TCP profile=private localport=5000,5100,9000,9099

ポートフォワーディングの設定

Vite も firebase emulators もサーバが WSL で動いてるので、Private Net での WinPC の IP アドレス → WSL の IP アドレスにポートフォワーディングする。サーバを WSL 上 で動かさないなら、この設定は不要。これも vscode 等の環境では自働で設定されます。

PowerShell(管理者権限)
> netsh interface portproxy add v4tov4 listenport=4000 connectaddress=172.30.156.231 connectport=4000
> netsh interface portproxy add v4tov4 listenport=5000 connectaddress=172.30.156.231 connectport=5000
> netsh interface portproxy add v4tov4 listenport=5100 connectaddress=172.30.156.231 connectport=5100
> netsh interface portproxy add v4tov4 listenport=9000 connectaddress=172.30.156.231 connectport=9000
> netsh interface portproxy add v4tov4 listenport=9099 connectaddress=172.30.156.231 connectport=9099

Vite サーバと firebase エミュレータを動かす

それぞれ別の WSL 端末で実行する。

WSL
$ npx vite --port=4000
WSL
$ firebase emulators:start

アプリに firebase エミュレータに接続するコードを書く

以下のように書いて各々のエミュレータに接続した。
アプリの動作環境に合わせて、window.location.href から接続先の IP アドレスを取る。
今回の構成例では、PC 上の Chrome から繋ぐ場合は 127.0.0.1[2]、他のホスト(Android/iOS)から繋ぐ場合は 192.168.40.18 になる。

firebaseエミュレータに接続するアプリのコード
import { getAuth, connectAuthEmulator } from "firebase/auth";
import { getDatabase, connectDatabaseEmulator } from "firebase/database";

function getHost(loc) {
  return loc.href.match(/https?:\/\/(.+?)[\:\/]/)?.at(1);
}
function getServer(loc, port) {
  return `http://${getHost(loc)}:${port}`;
}
const PORT_DATABASE = 9000;
const PORT_AUTH = 9099;

const app = initializeApp(config); // configの書き方は省略

const auth = getAuth();
const server = getServer(window.location, PORT_AUTH);
connectAuthEmulator(auth, server);

const db = getDatabase(app);
const host = getHost(window.location)
connectDatabaseEmulator(db, host, PORT_DATABASE);

[Optional] アプリを build する

dev 環境で Vite サーバに繋ぐなら不要だけど、firebase の Hosting サービスでのプレビューを試す場合は build する。

WSL
$ npm run build

ブラウザでアプリを動かす

それぞれの端末で Web ブラウザ(Chorome)を開き、Vite サーバが提供する URL にアクセスする。今回の構成例では、PC のブラウザでは http://localhost:4000 、Android/iOS 端末では http://192.168.40.18:4000 を開く[3]

以上の手順により、PC と Android については Auth エミュレータと Database エミュレータが期待通り動いた。ただし、試した API は以下で import しているメソッドのみ。

import { getAuth, GoogleAuthProvider, signInWithCredential, signInWithRedirect, getRedirectResult, signOut, onAuthStateChanged } from "firebase/auth";
import { getDatabase, ref, child, get, set, onValue } from "firebase/database";

アプリを firebase エミュレータに繋ぐと、以下のようにブラウザ画面の最下端に警告表示が出てくる。これが出てないときは、エミュレータに繋いでるつもりが実は本物の Google 認証に飛んではじかれてたりするので注意。

上のテスト画面で「Google でサインイン」を押すと GoogleAuthProvider で signInWithRedirect が呼び出され、下の画面に変わる。画面下端の警告表示は消えるけど、URL を見ると Auth エミュレータで動いてることが分かる。
ちなみに「Google でサインイン(ダミー認証)」のボタンは signInWithCredential を非対話型テスト[4]の方法で呼んでいる。この影響か、適当にいじってたら画面のように signWithRedirect の画面で同じメアドのアカウントが 3 つダブって出てくるようになってしまった。

何故か動かないやつ

iOS 端末では Auth エミュレータで signInWithRedirect できない

全く同じ手順で試していて、PC や Android では GoogleAuthProvider で signInWithRedirect のエミュレーションができたけど、iOS では駄目でした。Google 認証のエミュレーション画面には飛ぶけど、登録済みのアカウントを選択しても何故か認証されない。
deploy して本物の firebase ホスティング&本物の Google 認証なら問題なく動くので、iOS 版 Chorome と Auth エミュレータの組み合わせの問題かな..? 端末が初代 iPad Pro で古いのも気になるけど。解決したら追記します。

References

脚注
  1. 他のローカル デバイスからテストする ↩︎

  2. 何の設定だったか失念したけど、127.0.0.1 と localhost が区別される場合があったので注意。設定に localhost と書き、ブラウザの URL には 127.0.0.1 と書いてアクセスすると駄目な場合がある。 ↩︎

  3. firebase の Hosting エミュレータに繋ぐ場合はポート番号を 5000 に変える ↩︎

  4. (https://firebase.google.com/docs/emulator-suite/connect_auth?hl=ja&_gl=1#non-interactive_testing) ↩︎

GitHubで編集を提案

Discussion