はじめに
開発環境における検証作業で地味に時間がかかるのは、検証用アカウントの切り替えです。
クラウドサインには複数のプランやオプションがあり、機能によってはそれを切り替えながら検証する必要があります。これがなかなか大変です。
クラウドサインの通常の画面では、アカウントの切り替えは以下の手順で行います。
- 画面右上のプロフィールアイコンをクリック
- ログアウトをクリック
- ダイアログで「ログアウト」をクリック
- トップページに遷移するので、右上の「ログイン」をクリック
- メールアドレスを入力して「次へ」をクリック
- パスワードを入力して「ログイン」をクリック
- 完了
この間 20-30 秒程度かかります。これを頻繁に繰り返すと、かなりの時間が無駄になります。うんざりします。
ということで、Chrome 拡張を作って、ワンクリック1で検証用アカウントを切り替えられるようにしました。
ログイン情報を登録しておき、ボタンをクリックするとそのアカウントに自動でログインする、という仕組みです。
社内向けに公開もしており、それにまつわる話もします。
作っていく
開発の準備
今回は WXT というフレームワークを使用します。 WXT は Chrome 拡張を簡単に作成できるフレームワークで、TypeScript や Vue が使えます。 セットアップは公式ドキュメントに譲り、本記事では割愛します。
作るもの
今回作りたいものは、根幹としては以下の 2 つがあれば OK です。
- 選択したアカウントでログインを行う処理
- アカウントを選択できる UI
Chrome 拡張にはいくつかのコンポーネントがありますが、上記に最適なのはコンテンツスクリプトとポップアップ UI です。
- コンテンツスクリプトで画面のリソース(後述しますが CSRF トークン)を取得しつつログイン処理を実行する
- ポップアップ UI でアカウントを選択することで、そのアカウントのログイン処理を実行する
以上のような形にすればよさそうです。
Chrome 拡張といえばバックグラウンドスクリプトがつきものですが、今回は使用しません。画面リソースにアクセスできないからです。
ログイン処理を実装する
クラウドサインのログイン処理は、普通の REST API です。ヘッダに CSRF トークンをセットして、メールアドレスとパスワードを POST すればログインできます。
CSRF トークンはクラウドサインの画面に埋め込まれているので、拡張機能のコンテンツスクリプトで取得すれば OK です。
// content.ts export default defineContentScript({ // 安全のため、ステージング環境・ローカル環境のみに限定 matches: [ "*://<ステージング環境のドメイン>/*", "*://cloudsign.local/*", ], main() { async function switchAccount(email: string, password: string) { // CSRFトークンを取得 let token = document.body.dataset["token"]; // ログアウト await fetch("/api/logout", { method: "POST", headers: { "X-CSRF-TOKEN": token, }, }); // ログイン const data = new URLSearchParams(); data.append("email", email); data.append("password", password); await fetch("/api/login", { method: "POST", headers: { "X-CSRF-TOKEN": token, }, body: data, }); // リダイレクト window.location.href = "/dashboard"; } // メッセージリスナーを追加 browser.runtime.onMessage.addListener( async (message, sender, sendResponse) => { switch (message.action) { case "switchAccount": await switchAccount(message.email, message.password); sendResponse({ status: "success" }); break; } } ); }, });
ポップアップUIを実装する
本質は以下のコードです。選択されたアカウント情報をコンテンツスクリプトに送信しています。 Chrome 拡張ではコンポーネント間で関数を直接呼び出すのではなく、メッセージを使ってやりとりします。
// popup/App.vue const changeAccount = (account: LoginInfo) => { browser.tabs .query({ active: true, currentWindow: true }) .then((tabs) => { if (tabs[0].id) { browser.tabs.sendMessage(tabs[0].id, { action: "switchAccount", email: account.email, password: account.password, }); } }) .finally(() => { window.close(); }); };
アカウント情報の保持
WXT には localStorage のラッパーである storage APIが用意されているので、これを使用すればよいです。
パスワードは平文で保存しています。安全性を考えると通常では考えられませんが、以下の要因によって構造的にリスクが排除されるため許容と判断しています。
- ローカル環境: そのアカウントは手元の端末にしか存在せず、漏洩におけるリスクがない
- ステージング環境: これはインターネットからアクセス可能ですが、アクセスのために追加の Google 認証が必要で、アカウントの情報だけあってもログイン後の画面に到達不能
- 本番環境: 使用できないようコンテンツスクリプトで制限
当然パスワードの使いまわしをしていないことが前提ですが、このあたりはもはや常識であるという整理です。 社内の開発に関わるメンバーが想定利用者でありリテラシーに期待ができること、またパスワードを自動生成する機能も盛り込んで緩和策を講じていることも、この判断を後押ししています。
社内への公開
社内拡張機能といえば、Chrome ウェブストアに限定公開するパターンが定番ですが、当社では前例がなく実現には手続きが多そうだったので、ひとまずは ZIP 配布にしました。
またエンジニア向けには GitHub リポジトリも公開し、ソースコードからビルドできるようにしています。一度読み込んでしまえば pull & build で最新版に更新できるため、便利です。
効果と反響
効果
- アカウント切り替え操作 7 ステップ → 2 ステップ(拡張機能のパネルを開く、アカウントを選択する)
- 時間 20-30 秒 → 2-5 秒程度(拡張機能のパネルを操作する時間と通信時間。ローカルサーバーは重いので時間がかかる)
お客様2の声
- もう cs-account-changer がないと生きられない身体になってしまった
- Day03で発表されてた拡張機能が早速活躍中(神のリアク字)(天才のリアク字)
- この前発表してた「Cs Account Changer」めっちゃいい
思わぬ副産物
組織内のさまざまなメンバーがこの拡張機能を使うことで、有志が機能拡張・改善の PR をたびたび出してくれます。
そこでのやりとりを通じて、技術的な研鑽やチームビルディングにもつながっています。
フロントエンドになじみのないエンジニアやデザイナーも挑戦してくれ「テストは堅牢に書こう」とか「この CSS はこうやって書いたほうが本質に近い」とか、通常業務ではなかなか得られない学びがありました。
社内 OSS はいいぞ。
おわりに
開発環境のアカウント切り替えという小さな課題に対し、Chrome拡張で解決することで、時短だけでなくストレスの軽減にもつながりました。 さらに、社内OSSとして公開したことで、技術的な交流や学びの場としても機能しています。 小さな改善の積み重ねが、開発体験の向上につながることを実感した取り組みでした。
