メインコンテンツまでスキップ

第17章:Firestore内ロール管理パターン(やるなら注意点もセット)⚠️📦

この章はひとことで言うと👇 **「ロール(権限)を“Firestoreのドキュメント”として持つやり方」**を、事故らない形で理解して使えるようにする回です🛡️✨ (Custom Claimsの王道(第15〜16章)と比べて、便利さの代わりに地雷が増えるので、そこも含めてセットで行きます💣)


1) まず結論:Firestoreロール管理は“使いどころ”がある😼

Firestore内ロール管理が刺さるのは、だいたいこの2パターン👇

  • 権限が頻繁に変わる(今日だけモデレーター、明日解除…みたいな)🔁
  • **「プロジェクト別」「グループ別」**のように、権限が“リソース単位”で違う🏷️

一方で、**「管理者 / 一般ユーザー」みたいな“全体ロール”**は、Custom Claimsの方が堅くて軽いことが多いです🎫🔐 (Firebase) Comparison of Firestore Roles vs Custom Claims.


2) Firestoreロール管理の代表3パターン 🧩

パターンA:roles/{uid}(ユーザーごとのロール表)

例:roles/abc123{ admin: true, editor: false } みたいに入れる。

  • 👍 直感的で分かりやすい
  • 👎 Rulesの中で get() / exists() 参照が増えがち(=コスト&制限に影響)(Firebase) Pattern A: Roles Document.

パターンB:projects/{projectId}/members/{uid}(“所属”をドキュメントで表現)

例:メンバーであることを exists(/projects/p1/members/abc123) で判定。

  • 👍 “プロジェクト単位”権限に強い
  • 👍 ユーザー全体ロールより自然
  • 👎 設計を雑にすると「membersを書き換えて昇格😱」が起きる Pattern B: Project Membership.

パターンC:groups/{groupId}members: [uid...](配列で持つ)

  • 👍 ルールは書きやすい
  • 👎 人数が増えると地獄になりやすい(ドキュメントサイズ上限、更新競合、管理がつらい) なので初心者向けには「Bが一番安全寄り」でおすすめです🙂

3) ここが地雷:Firestoreロール管理の“事故ポイント集”💥

地雷①:ロール情報をクライアントに書かせる(即アウト)🙅‍♂️

ロールをFirestoreに置くなら、ロールを更新する権限は超・限定しないと詰みます😇 Rulesで「rolesは誰も書けない」または「管理者だけ」みたいに固定します。 Danger of Client-side Role Writing.

地雷②:get() / exists() は“追加の読み取り課金”になる💸

Rules内で get() / exists() / getAfter() を使うと、Rules評価のための追加Readが発生します(拒否されても発生し得る)😱 (Firebase) Cost of Rules Read Operations.

地雷③:get() / exists() の呼び出し回数には上限がある🚧

1リクエストあたりの上限が決まってます👇

  • 単一ドキュメント読み取り / クエリ:10回
  • 複数ドキュメント読み取り / トランザクション / バッチ書き込み:20回(ただし各操作あたり10回の制限も意識) (Firebase)

つまり「membersをシャーディングしていっぱい exists()」みたいなのは、割とすぐ限界に当たります😵

地雷④:サーバー側ライブラリはRulesをバイパスする🕳️

ロール更新をサーバーでやるのは正解なんだけど、サーバー用ライブラリ(Admin/Server SDK)はRulesを通りません。 なので「サーバー側の権限」はIAMなど別の守りが必要です🧯 (Firebase)


4) ハンズオン:安全な “rolesドキュメント参照” を作る🧪🔥

今回は分かりやすい パターンA(roles/{uid}) で行きます。 (この作りを理解すると、Bにも応用できます🙂)

4-1) 目標 🎯

  • adminOnlyLogs/{logId}adminだけ read/write OK 👑
  • publicPosts/{postId}ログインしてれば read OK(書き込みは別条件でもOK)

5) ルール実装(テンプレ)🛡️

ポイントはこれ👇

  • roles/{uid} 自体は クライアントから読めなくてOK(むしろ読ませない)🙈
  • Rulesの中では exists() / get() で参照して判定する
  • 失敗時に落ちないように exists() を先に置く
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {

function signedIn() {
return request.auth != null;
}

function roleDocPath(uid) {
return /databases/$(database)/documents/roles/$(uid);
}

function isAdmin() {
return signedIn()
&& exists(roleDocPath(request.auth.uid))
&& get(roleDocPath(request.auth.uid)).data.admin == true;
}

// 🔒 roles は “誰にも直接触らせない”
match /roles/{uid} {
allow read, write: if false;
}

// 👑 管理者だけ触れるログ
match /adminOnlyLogs/{logId} {
allow read, write: if isAdmin();
}

// 🙂 公開投稿:ログインしてれば読める(例)
match /publicPosts/{postId} {
allow read: if signedIn();
allow write: if false; // ここはアプリ要件で調整してOK
}
}
}

exists() / get() は追加Readになり得る&回数制限もあるので、「isAdmin() を1回で済む形」に寄せるのが大事です💡 (Firebase) Logic of isAdmin function.


6) テスト:Emulatorで“通る✅/弾く❌”を固定する🧪

Rulesは テストが本体です🙂 Firebase公式も、Emulatorでユニットテストを回す流れを強く推しています。(Firebase)

6-1) テスト用ライブラリ

@firebase/rules-unit-testing を使います(v9系のテストが推奨)(Firebase) ※最近の変更で Nodeの最小バージョンが引き上げられているので、プロジェクトのNodeは新しめで👍 (GitHub)

6-2) テストでやること(最小セット)

  • ❌ 未ログイン:adminOnlyLogs は読めない
  • ❌ 一般ユーザー:adminOnlyLogs は読めない
  • ✅ admin:adminOnlyLogs を読める&書ける

(※ admin判定のために roles/{uid}テスト側で事前に書いておく


7) AIで加速:Rules&テストを“叩き台生成→人間が締める”🤖🧑‍⚖️

7-1) Gemini CLI / Antigravity で何ができる?⚡

  • Firebase MCP server は、Antigravity / Gemini CLI / Firebase Studio などのMCPクライアントから使えます。(Firebase)
  • Gemini CLIには Firebase拡張があり、Firebase向けの能力とプロンプト群が使えます。(Firebase)
  • さらに「Write security rules」プロンプトは、ソースコードやスキーマを解析してRulesとテストの叩き台を作り、攻撃シミュレーションで弱点を探す方向の設計になっています(ただし制限もあるので過信はNG)(Firebase)

7-2) 使い方のコツ(事故らない線引き)🧠

AIにやらせるのは👇

  • ルールの雛形(match構造、関数化、deny by default)
  • テストケースの列挙(未ログイン/一般/admin、想定攻撃パターン)

人間が必ず握るのは👇

  • ロールの置き場所(rolesなのかmembersなのか)
  • ロール更新の責任者(誰が更新できるか)
  • get() / exists() をどこまで許すか(コスト&上限)(Firebase)

8) ミニ課題 🎯(10〜15分)

次の2択で、自分のアプリに寄せて考えてみてください🙂

  1. roles方式(A)
  • roles/{uid}{ admin: true } を入れる設計にして、 「rolesは誰もread/write不可」にした上で、adminOnlyLogs を守る🛡️
  1. members方式(B)
  • projects/{projectId}/members/{uid} を作る設計にして、 「membersは本人が勝手に作れない」ようにする(ここが腕の見せ所🔥)

9) チェック✅(この章のゴール)

  • get() / exists()追加課金&回数制限があるのを説明できる💸🚧 (Firebase)
  • 「rolesドキュメントをクライアントに書かせたら終わる」理由が言える😇
  • Emulatorテストで、未ログイン/一般/admin の3者が 毎回同じ判定になる🧪 (Firebase)

10) おまけ:ロール更新をサーバーでやる時の“バージョン目安”🧾✨

ロール付与や更新はサーバー側でやるのが基本です(例:Admin SDKでCustom Claims付与、またはroles更新など)🎫

  • Cloud Functions(Node):Node.js 20 / 22 がサポート、18はdeprecated扱い (Firebase)
  • Admin SDK(Python):Python 3.10+ 推奨(3.9はdeprecated) (Firebase)
  • Admin SDK(.NET):.NET 8+ 推奨(6/7はdeprecated) (Firebase)

次の第18章は、この章で触れた地雷を「事故パターン集」としてまとめて、見た瞬間にツッコめる力を作っていきます😂💥