🆔

UIDをドキュメントIDの代わりに使う理由

2024/05/15に公開

詳しく書いてみようと思った...

詳しくと記事のタイトルに書いてしまいましたが、実際のところは、これで詳しくというのは、個人の考えです🤔
結論をいうと、Firestoreのセキュリティールールを書きやすくするためなんですよね。

なぜ書きやすくなるのか?

Firestoreにデータを保存すると、ランダムなIDが作られるじゃないですか💁
collection.add()ですね。でもこれだと、誰のデータか特定することができないですね。だってランダムに生成されているんだもの!

Firestoreを使うときですが、基本はAuthenticationと組み合わせます。
collection.doc(uid).set()だと特定のユーザーのデータとして保存することができます。これは1個のデータを1度けしか保存しません。2回目に実行するとupdate(上書き)されます。

公式にも書いてあるかな?
https://firebase.google.com/docs/rules/basics?hl=ja#production-ready_rules

これは、認証済みのコンテンツ所有者にのみアクセスを制限するというルールです。データの読み取りや書き込みを行うことができるのは 1 人のユーザーに限られます。そのユーザーの ID がデータパスに含まれています。

このルールが機能するケース: このルールが適切に機能するのは、ユーザーによってデータがサイロ化されている場合です。つまり、データにアクセスする必要がある唯一のユーザーが、そのデータを作成した本人である場合です。

このルールが機能しないケース: このルールセットが機能しないのは、同じデータに対して複数のユーザーが書き込みや読み取りを行う必要がある場合です。つまり、複数のユーザーがデータを上書きしようとする場合、ユーザー自身が作成したデータでもそれにアクセスできなくなってしまいます。

このルールを設定する: データの読み取りや書き込みへのアクセスをリクエストしているユーザーがそのデータの所有者であることを確認するルールを作成します。

service cloud.firestore {
  match /databases/{database}/documents {
    // Allow only authenticated content owners access
    match /some_collection/{userId}/{documents=**} {
      allow read, write: if request.auth != null && request.auth.uid == userId
    }
  }
}

例を出すと、こんな感じのコードになりますね

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:uuid_application/model/post_model.dart';

class ServiceClass {
  final db = FirebaseFirestore.instance;
  final auth = FirebaseAuth.instance;

  Future<void> addPost(String nameController) async {
    final uid = auth.currentUser?.uid;
    final newPost = PostModel(
      id: uid,
      name: nameController,
    );
    await db.collection('post').doc(uid).set(newPost.toMap());
  }
}

setを使う場合だと、ユーザーのuidを指定して、Firestoreにデータを保存するので、ドキュメントIDが、ランダムなものではなくて、ユーザーアカウントを作成したときのuidが、ドキュメントIDとなります。
taro@co.jpのユーザーUIDが使われる

ログインしてデータを保存してみた!

「おっ一致してますね。」これが、ドキュメントIDとユーザーのuidを一致させるということか!
なるほどなるほど。

こちらがセキュリティールールです

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    // postコレクションにログインしているユーザーが書き込み可能
    match /post/{uid} {
      // ドキュメントIDと書き込みリクエストした認証ユーザーが一致していれば許可
      allow create: if request.auth != null;
    }
  }
}

ユーザーがログインしていたら、データを保存する(書き込みと本では表現されていた)ことができます。
コンソールで、ダミーのデータを使ってセキュリティールールが正しく動いているのかテストすることができます。

セキュリティールールのテストをしてみる

画面左の星マークをクリックしてください。
ルールプレイグランドなるものがありますね。こちらをクリックしてみましょう。そしたら、入力するところがいっぱい出てきましたね。
こちらの項目を選択したり、記入をしてテストができます。

やり方

  1. 今回やるのは、データの保存なので、シュミレーションタイプをcreateにする。
  2. 場所のところは、パスを指定します。パスってのは、コレクション名/フィールド名です。今回だと、postコレクションのnameフィールドのことですね。
  3. ドキュメントを作成のボタンを押して保存するダミーのデータのフィールド名、データの型、値を記入して、完了を押します。
  4. プロバイダは、google.comのままで、FirebaseUIDは、taro@co.jpさんという方のユーザーUIDを指定します。メールは、taro@co.jpを指定します。
  5. 名前、電話の項目は、空白のままで、実行ボタンを押します。

成功すると、緑色のログが表示されます。失敗したら赤いログが出てくて、間違ったセキュリティールールを書いているコードの部分に × 印がつきます!

最後に

以前こんな記事を書いていたのを思い出しました。
https://zenn.dev/joo_hashi/articles/b754e4ef08f2fb
久しぶりに見てみると、なんでドキュメントIDとUIDを一致させるのかについて解説していませんでした。当時から、セキュリティールールを書きやすくするために、使うということは知っていました。

こちらが今回使用したサンプルコード
状態管理は、していなくて初心者向けのログインとデータの追加を学べるだけのサンプルです。
StatefulWidgetとStatelessWidgetしか使ったことないよという人には、丁度良いかもしれません?
昔の私は、モデルクラスすら作ってませんでしたよ😅
https://github.com/sakurakotubaki/UUID-Aplication

Discussion