第7章:ユーザー別データ設計(uid基準の王道)👤📁✨
この章のゴールはコレ👇 「ログインした人が“自分のデータだけ”読める/書ける」状態を、Rulesでカチッと作ることです🔐💪 そして…他人のuidをURLみたいに指定しても、ちゃんと弾けるようにします🚫😤
1) まず“王道”を1枚で理解しよう🗺️✨
FirestoreのRulesは、基本 **「パス(場所)+条件」**で守ります🧱 つまり、データの置き方がキレイだと、Rulesもキレイになります🙂✨
✅ 王道の設計(超おすすめ)👑

- users/{uid} … そのユーザー本人の“マイページの本体”📄
- users/{uid}/(subcollections) … その人の個別データ(メモ、設定、下書き…)📦
- publicProfiles/{uid} … 公開プロフィール(読むのは全員OK、書くのは本人だけ)🌍🧑💻
ここで大事なのがコレ👇 uidを「ドキュメントID」にすること! そうするとRulesが「本人かどうか」を判断しやすいんです😆
Rules側では、認証情報として auth.uid(ユーザー固有ID) が使えます。(Firebase)
(Rules言語の基本構造は match と allow で作ります🧩)(Firebase)
2) ありがちな失敗パターン(先に踏んで回避😂💥)
❌ 失敗A:uidを“フィールド”に入れて所有者判定しようとする

例:posts/{postId} の中に ownerUid: "xxx" を入れて、Rulesで request.auth.uid == resource.data.ownerUid みたいにするやつ。
これ、正しく書けば可能なんだけど、初心者がやると事故りやすいです😇 理由は👇
- 「作成時に ownerUid を好きに書かれる」事故が起きる💣
- 「更新で ownerUid を書き換えられる」事故が起きる💣💣
👉 まずは パスにuidを埋める設計から入るのが安全です🛡️✨
❌ 失敗B:auth != null だけでOKにしちゃう

「ログインしてればOK」だけだと、**“全ログインユーザーが他人のデータ触れる”**状態になりがち😱 公式にも「authだけチェックして満足してない?」って注意があります⚠️(Firebase)
3) Hands-on:データ構造を作る🧑💻📁
今回はこの形にします👇
-
users/{uid}(非公開・本人だけ)displayNamephotoURLcreatedAtupdatedAt
-
publicProfiles/{uid}(公開・読むのは全員OK、更新は本人だけ)displayNamephotoURLupdatedAt
-
users/{uid}/notes/{noteId}(非公開メモ・本人だけ)textupdatedAt
4) Rulesを書こう(まずは“最小で強い”やつ)🛡️✨

ポイントはこれだけ👇 「ログインしていて、かつ uid が一致する」=本人✅
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
function signedIn() {
return request.auth != null;
}
function isOwner(uid) {
return signedIn() && request.auth.uid == uid;
}
// ✅ 本人だけのデータ(超王道)
match /users/{uid} {
allow read, write: if isOwner(uid);
}
// ✅ 本人だけが書けて、読むのは全員OK(公開プロフィール)
match /publicProfiles/{uid} {
allow read: if true;
allow create, update: if isOwner(uid);
allow delete: if false;
}
// ✅ 本人だけのサブコレ(メモなど)
match /users/{uid}/notes/{noteId} {
allow read, write: if isOwner(uid);
}
}
}
rules_version = '2'(v2が最新)も忘れずにね🙂(Firebase)
5) React(TypeScript)で「自分のusers/{uid}」を触る👆✨

「ドキュメントID=uid」にするのがコツです👑 (自動IDにしない!)
import { getAuth } from "firebase/auth";
import { getFirestore, doc, getDoc, setDoc, serverTimestamp } from "firebase/firestore";
const auth = getAuth();
const db = getFirestore();
export async function upsertMyUserProfile(displayName: string, photoURL?: string) {
const user = auth.currentUser;
if (!user) throw new Error("Not signed in");
const ref = doc(db, "users", user.uid);
await setDoc(
ref,
{
displayName,
photoURL: photoURL ?? null,
updatedAt: serverTimestamp(),
createdAt: serverTimestamp(), // 初回以外は上書きされちゃうのが気になるなら後で改善OK🙂
},
{ merge: true }
);
}
export async function readMyUserProfile() {
const user = auth.currentUser;
if (!user) throw new Error("Not signed in");
const ref = doc(db, "users", user.uid);
const snap = await getDoc(ref);
return snap.exists() ? snap.data() : null;
}
6) “他人のuidで読めない”を確認しよう(ここが本番😤🧪)

手で確認(超シンプル)🖱️
- Aユーザーでログインして
users/{Aのuid}を読む ✅ - コードをわざと改造して
users/{Bのuid}を読もうとする - Permission denied になったら勝ち🏆✨
7) Emulatorでテスト(安全開発の必殺技)🧪🧯
Rulesは「書いて終わり」じゃなくて、テストしてから出すが正解です✅ 公式もEmulatorでの検証&単体テストを推しています🧪(Firebase)
まずEmulator起動🚀
firebase emulators:start --only firestore,auth
(Emulatorのセットアップや起動はこの流れが基本です)(Firebase)
Rulesの単体テスト例(Jest想定)🧪

「Aliceは自分のusers/Aliceは読める、Bobのusers/Bobは読めない」を機械で保証します🤖✅
import fs from "node:fs";
import { initializeTestEnvironment, assertFails, assertSucceeds } from "@firebase/rules-unit-testing";
import { doc, getDoc, setDoc } from "firebase/firestore";
test("users/{uid} is private to the owner", async () => {
const testEnv = await initializeTestEnvironment({
projectId: "demo-rules-ch7",
firestore: {
rules: fs.readFileSync("firestore.rules", "utf8"),
},
});
const alice = testEnv.authenticatedContext("aliceUid");
const bob = testEnv.authenticatedContext("bobUid");
const aliceDb = alice.firestore();
const bobDb = bob.firestore();
// ✅ Aliceは自分のドキュメントを書ける
await assertSucceeds(setDoc(doc(aliceDb, "users", "aliceUid"), { displayName: "Alice" }));
// ✅ Aliceは自分のドキュメントを読める
await assertSucceeds(getDoc(doc(aliceDb, "users", "aliceUid")));
// ❌ BobはAliceのドキュメントを読めない
await assertFails(getDoc(doc(bobDb, "users", "aliceUid")));
await testEnv.cleanup();
});
(単体テストの考え方と環境づくりは公式ガイドが一番確実です)(Firebase)
8) AI活用:Rulesとテストの“叩き台”を爆速で作る🤖⚡
✅ 使い分けのコツ(超大事)🧠
- AI:叩き台を作る(構造、ルールの候補、テストの雛形)🧱
- 人間:最終判断&レビュー(漏れ、意図、事故ポイント)👀🧑⚖️
Gemini CLI(Firebase拡張)でRules生成🛠️
FirebaseのAI支援では、**Gemini CLI の拡張がプロジェクト状態を見てRulesを“1回生成”**できます。 ただし 自動追従で更新されるわけじゃないので、更新は自分でやる必要ありです⚠️(Firebase)
さらに、AIプロンプト集も用意されていて、MCP対応のAIアシスタントから使える流れになっています📚✨(Firebase)
👉 AIに投げるおすすめ指示(そのままコピペOK)👇
- 「
users/{uid}は本人だけ read/write」 - 「
publicProfiles/{uid}は read=全員、write=本人」 - 「
users/{uid}/notes/{noteId}は本人だけ」 - 「Emulator用の単体テスト(owner/other/unauth)も作って」🧪
Antigravity + Firebase MCP で、状況を見ながら相談&整備🧲✨
Antigravityに Firebase MCP server を入れると、エージェントがFirebase手順や状態に沿って作業しやすくなります(導入手順も公式で案内あり)(The Firebase Blog)
Gemini in Firebase の立ち位置🙂
Gemini in Firebase はRulesの相談はできるけど、コードベースにアクセスできないのでRulesを“生成”する用途はGemini CLI推奨、という整理です🧠(Firebase)
9) ミニ課題🎯✨(10〜20分想定)
課題A:非公開プロフィール(users/{uid})
displayNameを更新できるUIを作る🖊️- devtools等で“他人uid”に差し替えて読みに行って、弾かれるのを確認🚫😤
課題B:公開プロフィール(publicProfiles/{uid})
- 一覧画面(全員のpublicProfilesを読む)を作る🌍
- でも更新は本人だけ(他人のpublicProfiles/{uid}更新が弾かれる)✅
10) チェック✅(ここまでできたら勝ち🏆)
-
users/{uid}が 本人以外は読めない/書けない🔒 -
publicProfiles/{uid}が 読むのは全員OK、更新は本人だけ🌍✍️ - “uidを差し替える攻撃”を試しても Permission denied になる💥
- Emulatorテストで ✅/❌ が自動で確認できる🧪
必要なら、次の一歩として「users/{uid} の中でも フィールドごとに更新可否を分ける(例:createdAtは絶対変更不可)」みたいな“事故りにくさMAX”設計も、この章の続きとして作れます😆🛡️