FirebaseのセキュリティルールをWHEREのように使えると思っていた


Nuxt.js+Firebaseの勉強を始めたばかりです。
みなさんの記事を見よう見まねで、WebページでFirestoreのデータベースにデータを登録できるところまで作成しました。

前回: Nuxt.js+Firebaseでログイン情報を保持したい

やりたいこと

Firestoreのデータを誰でも閲覧できてしまっては困るので、ログインユーザーごとにデータを出し分けたいと思います。

環境

  • Firebase 7.3.0
  • Vue CLI 4.0.5
  • Nuxt.js 2.10.2

セキュリティルールを変更したが...

Firestoreの構成は次の通りです。

コレクション ドキュメント フィールド
notes (uid) ・content

ログインユーザーのuidとドキュメントのuidとが一致するデータを画面に表示したかったので、Firestoreのセキュリティルールを次のようにしました。

firestore.rules
service cloud.firestore {
  match /databases/{database}/documents {
    match /notes/{userId} {
      function isOwner() {   
        return request.auth.uid == userId;
      }

      allow update, delete: if isOwner();
      allow create: if request.auth.uid != null;
      allow read: if isOwner();
    }
  }
}

Webページを開いてみたら取得結果が0件になっていました。ログインユーザーが登録したデータも表示されません。

セキュリティルールはフィルターではない

FirestoreのセキュリティルールをWHEREのように使えると思っていました。
取得結果が0件になったのは、Firestoreの検索結果に ログインユーザーのuidとドキュメントのuidとが一致しないデータ が含まれていたからです。

修正前のpages/index.vue
  mounted () {
    this.$store.dispatch('setNotesRef', db.collection('notes'));
  },
修正後のpages/index.vue
  mounted () {
    let userID = this.$store.getters['auth/getUid'];
    this.$store.dispatch('setNotesRef', db.collection('notes').doc(userID));
  },

db.collection('notes').doc(userID) には ログインユーザーのuidとドキュメントのuidとが一致しないデータ は含まれていない、つまりルールに一致するデータのみなので、Firestoreからログインユーザーのデータだけを取り出すことができました。

参考までに~/store下はこんな感じです。

store/auth.js
import Vue from 'vue';
import { auth } from '~/plugins/firebase';

export const state = () => ({
        user: {},
        status: ""
    });

export const mutations = {
        setUser(state, user) {
            state.status = "loggedIn";
            state.user = user;
        },
        logout(state) {
            state.status = "loggedOut";
            state.user = {};
        }
    };

export const getters = {
        isLoggedIn: (state) => {
          return state.status === "loggedIn";
        },
        getUsername: (state) => state.user.displayName,
        getUid: (state) => state.user.uid
    };

export const actions = {
        gotUser({ commit }, user) {
            commit("setUser", user);
        },
        logout({ commit }) {
            auth.signOut().then(() => {
                commit("logout");
            })
        },
    };
store/index.js
import { vuexfireMutations, firestoreAction } from 'vuexfire';
import createPersistedState from "vuex-persistedstate";

export const state = () => ({
      notes: [],
    });

export const mutations = {
      ...vuexfireMutations
    };

export const getters = {
      getNotes: (state) => {
        return state.notes;
      },
    };

export const actions = {
      nuxtClientInit ({ commit, state, dispatch }, { req }) {
        createPersistedState()(this);
      },
      setNotesRef: firestoreAction(({ bindFirestoreRef }, ref) => {
        bindFirestoreRef('notes', ref);
      }),
    };

まとめ

今回学んだことのまとめです。

  • Firestoreのセキュリティルールはフィルターではない
  • Firestoreのセキュリティルール + 検索条件のコーディングをする