Firebase【セキュリティールール編】

# 概要
Firestore/Storageでのセキュリティールーのメモ

[セキュリティ ルール言語](https://firebase.google.com/docs/rules/rules-language?authuser=0) 
[カスタム トークンを作成](https://firebase.google.com/docs/auth/admin/create-custom-tokens?hl=ja) 
[セキュリティ ルールと Firebase Authentication](https://firebase.google.com/docs/rules/rules-and-auth?authuser=0)  

# セキュリティールール構文
・`service` 
Firebase プロダクト（サービス）を定義する。 
・`macth` 
firestoreまたはstorageへのpathのパターンを宣言する。 
・`allow` 
ルールのいずれかが一致すると、指定したメソッドを実行できる。

## 関数定義
処理をまとめて関数化することができる

```js
// return boolean
fuction example() {
    return request.auth.uid != null
}
```

## 組み込み変数・関数
※ここではよく使いそうなものだけ抜粋。  

・`request`   
request.autn 認証済みであればパラメーターが入ってくる。  
・`exists()`  
引数にpathを渡すと存在するかどうかを返す。  
・`get()`  
引数にpathを渡すとオブジェクト返す。


## セキュリティ ルールのメソッド
<table>
<tr>
  <th>メソッド</th>
  <th>動作</th>
  <th>備考</th>
</tr>
<tr>
  <td>read</td>
  <td>読み取り</td>
  <td>あらゆるタイプの読み取りリクエスト</td>
</tr>
<tr>
  <td>write</td>
  <td>書き込み</td>
  <td>あらゆるタイプの書き込みリクエスト</td>
</tr>
<tr>
  <td>get</td>
  <td>読み取り</td>
  <td>単一のドキュメントやファイルを対象とした読み取りリクエスト</td>
</tr>
<tr>
  <td>list</td>
  <td>読み取り</td>
  <td>クエリとコレクションを対象とした読み取りリクエスト</td>
</tr>
<tr>
  <td>create</td>
  <td>書き込み</td>
  <td>新しいドキュメントやファイルへの書き込み</td>
</tr>
<tr>
  <td>update</td>
  <td>書き込み</td>
  <td>既存のドキュメントやファイルへの書き込み</td>
<tr>
  <td>delete</td>
  <td>削除</td>
  <td>データの削除</td>
</tr>
</table>

## ルールサンプル
１、firestorageで認証しているとコレクション(users)に書き込みができる 
２、コレクション(users)で認証IDと同じドキュメント名のデーターのみ読み取りができる。
3、firestorageで認証しているとコレクション(books)に書き込み、読み取りができる 

```js
service cloud.firestore {
    // ここはおまじない
    match /databases/{database}/documents {
        // 関数を定義
        function signedIn() {
            return request.auth != null
        }
        // 1
        match /users {
            allow write : if signedIn()
        }
        // 2
        match /users/{uid} {
            allow read : if request.auth.uid == uid
        }
        // 3
        match /books {
            allow read write : if signedIn()
        }
    }
}
```

macthはネストすることもできる

```js
service cloud.firestore {
    match /databases/{database}/documents {
        function signedIn() {
            return request.auth != null
        }
        // 1
        match /users {
            allow write : if signedIn()
            // 2
            match /{uid} {
                allow read : if request.auth.uid == uid
            }
        }
        // 3
        match /books {
            allow read write : if signedIn()
        }
    }
}
```

## 備考
・アプリへの認証にFirebase Authenticationを使用せずにサーバー(PHPやNode.jsなど)でログインしている場合
→ サーバー側でトークンを発行、firebase-adminの`createCustomToken`でトークンを生成しクライアント側に渡し`signInWithCustomToken`でログインする。

サンプル：

```js:サーバー側
// ... //
const admin = require('firebase-admin');
admin.initializeApp({
    credential: XXXX,
    databaseURL: XXXX
});

// ... //
const token = "<トークン>";
admin.auth().createCustomToken(token)
    .then(customToken => {
    // 生成したカスタムトークンをクライアント側に送信する処理
    })
    .catch((error) => {
    // error
    })
```

```js:クライアント側
import firebase from "firebase/app";
import 'firebase/auth'
// ... //
const customToken = "<サーバー側で生成したカスタムトークン>";
firebase.auth().signInWithCustomToken(customToken)
    .catch((error) => {
        // error
    })