firebaseのAuthenticationとfirestoreを利用したセキュリティルール設定
ここではセキュリティルールを考えた際にfirestoreはどういったデータ構造にするか、ということを説明する。
詳細の設定については、 公式ブログ に譲る。
チャットアプリのような双方向の通信を実現する際は、 firebase
を利用することで簡単に実装できる。
実際には、アプリケーション内で firestore
のデータを監視して、データの追加/変更/削除が発生したら自身で用意したイベントを呼び出すことで、相手の 発信
を受け取ることができる。
ただ、 firestore
は正しいセキュリティルールを設定しないと、 誰でも ストレージにアクセスができるので、必ずルールを設定しなければいけない。
ここで、 firestore
を以下のように組み立てるとする(これは後に困るやり方)。
Collection | Document | Collection/data | Document |
---|---|---|---|
chatRoom | XXX | member(C) | YYY |
message(C) | ZZZ | ||
name(d) | |||
description(d) |
アクセス制御を行うには、以下のような設定を行う。
match /chatRoom/{roomId} { allow read, write: if exists(/databases/$(database)/documents/chatRoom/$(roomId)/member/$(request.auth.uid)); } // 上記以外全て拒否
このようにすることで、 /chatRoom
にアクセスして全データを取得するときに、 /chatRoom/{roomId}
の条件にヒットしないものは取得されない、と思ったが、このパス /chatRoom/{roomId}
はリクエストパスを表現しているため、 このようにアクセス制御を行うと、 /chatRoom
へのルールが設定されていないため、アクセス自体が拒否されてしまう。 /chatRoom
の全取得で権限があるデータだけを取得する、という操作はできないことがわかる。
このことから、 firestore
のデータ構造を以下のようにする必要がある。user
コレクションには、 chatRoom
の情報を非正規化して保持している。ただ、非正規化が行われるということは、チャットルーム名の変更などが発生した場合は、非正規化が発生した部分を全て変更しなければならない。firebaseでは、 collectionGroup()
というメソッドでサブコレクションを串刺しにして取得することができるので、collectionGroup(YYY)
とすることで、非正規化されたデータを一括で書き換えることができるようになる。
(※ user
のサブコレクションの userRoom
を room
にしなかった理由はサブコレクションを串刺しにしたいときに、 room
は他のサブコレクション名とバッティングする恐れがあるため、 userRoom
とした)
Collection | Document | Collection/data | Document | data |
---|---|---|---|---|
chatRoom | XXX | message(C) | ZZZ | |
name(d) | ||||
description(d) | ||||
user | XXX | userRoom(C) | YYY | name(d) |
description(d) |
このようにすることで、チャット一覧の取得は、 /user/{user}/userRoom
で取得するようになる。このとき、認証されたユーザしか /user/{user}/userRoom
のデータにアクセスできない。チャットの詳細を取得するときは、 /chatRoom/{roomId}
にアクセスして、認証されているユーザがこのルームへのアクセス権限があるかを判定している。
match /user/{user}/userRoom { allow read, write: if user == request.auth.uid; } match /chatRoom/{roomId} { allow read, write: if exists(/databases/$(database)/documents/user/$(request.auth.uid)/userRoom/$(roomId)); } // 上記以外全て拒否
これでセキュアな状態(非正規化ではあるもののデータ更新に困らない)のモノになるのではないだろうか。