App Checkを使って特定のアプリケーションのみCloud Firestoreへのアクセスを許可する

# はじめに
タイトル通りですが、この記事は`Cloud Firestore`で特定のアプリケーションのみアクセスを許可する方法について書いています。設定方法はiOS、Android、Webとプラットフォームごとに違いますが、ここではWebについてのみ説明しています。
また、サンプルコードでは`Vue.js`、`Javascript`を使用しています。

# 前提
* Firebaseプロジェクトを作成済み
* FirebaseにWebアプリを作成済み
* Cloud Firestoreを構築済み

# App Checkとは
> App Check は、未承認のクライアントがバックエンド リソースにアクセスするのを防ぐことで、API リソースを不正使用から保護します。Firebase サービス、Google Cloud サービス、およびお客様の API を連携して、リソースを安全に保ちます。

上記は公式ドキュメントから引用文です。  
`App Check`は指定したクライアントのみ`Firebase`のリソースにアクセスできるように設定するためのサービスになります。

https://firebase.google.com/docs/app-check?hl=ja

`Cloud Firestore`は接続情報さえあれば誰でもアクセスできるのですが、この情報は色々なところから誰でも見られるようになっています。このことについては下記の記事がとても参考になりました。

https://zenn.dev/yucatio/articles/5427eede6c3e34

上記のような理由からアクセスの制限を行う必要があり、それをApp Checkで行うことができます。

# Cloud FirestoreとApp Checkの連携方法
![App Check (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1046681/2f3785ed-2da5-dc96-e46f-64ed073f84be.png)

各種設定を行うと最終的に上記のような状態になります。(正確に表せていない可能性がありますのであくまでもイメージと思っていただけると幸いです)
`App Check`を設定したサービスはリクエストする際に専用のトークンを含まないとアクセスできなくなります。トークンを取得するために図の①と②のプロセスが必要になります。このプロセスは全て`App Check SDK`が自動で行ってくれるのであまり意識する必要はありません。

図のような状態にするには以下の4つのことを行う必要があります。
1. reCAPTCHA Enterpriseのサイトキーを発行する
1. App Checkでウェブアプリに対してreCAPTCHA Enterpriseのサイトキーを設定する
1. Cloud FirestoreにApp Checkを適用する
1. App Check SDKをアプリケーションに導入する

それぞれ説明していきます。

## reCAPTCHA Enterpriseのサイトキーを発行する
以下の手順で`reCAPTCHA Enterprise`を設定し、キーを発行します。
1. 下記にアクセスする
https://console.cloud.google.com/security/recaptcha?project=_&hl=ja
1. プロジェクト選択画面で設定したいプロジェクトを選ぶ
1. `reCAPTCHA Enterprise API`の画面で青色の「有効にする」ボタンを押す
1. 遷移後の画面で「キーを作成」押す
1. プラットフォームの種類は「ウェブサイト」を選択し、ドメインを追加する。(ローカル環境の場合「localhost」のみでよい)
1. キーIDをメモする

## App Checkでウェブアプリに対してreCAPTCHA Enterpriseのサイトキーを設定する
`Firebase`の管理画面にアクセスし、以下の手順で設定を行う。
1. 左のナビゲーションから「全てのプロダクト」を選択
1. 「構築」のブロックの「App Check」を選択
1. `App Check`の画面で「始める」を押す
1. `App Check`の設定画面で「アプリ」のタブを押して、設定するアプリを選択する
1. 証明書プロバイダ選択画面で`reCAPTCHA Enterprise`を選択する
1. サイトキーの入力フォームにメモしておいたキーIDを入力し、保存する

## Cloud FirestoreにApp Checkを適用する
引き続き`App Check`の設定画面で以下の作業を行います。
1. `App Check`の設定画面で「API」のタブを押して、「Cloud Firestore」を選択する
1. ダイアログが開くので「適用」ボタンを押すと、さらにダイアログが開くので同じように「適用」ボタンを押す

## App CheckのSDKを導入
アプリケーションに対してSDKを導入していきます。言語はJavascriptで、npm等を使ってライブラリ使用する想定です。
下記のコマンドで`firebase`のライブラリを導入すれば`App Check`のライブラリも使用することができます。
```js
npm install firebase
```

次のソースコードはApp Checkの初期化を行うためのもので、公式ドキュメントから引用しました。
`new ReCaptchaEnterpriseProvider`の引数に`reCAPTHA Enterprize`のキーIDを設定します。`isTokenAutoRefreshEnabled`はトークンを自動的に更新するかどうかを設定するもので、`true`だと自動で更新してくれるようになります。

```js
import { initializeApp } from "firebase/app";
import { initializeAppCheck, ReCaptchaEnterpriseProvider } from "firebase/app-check";

const app = initializeApp({
  // Your Firebase configuration object.
});

// Create a ReCaptchaEnterpriseProvider instance using your reCAPTCHA Enterprise
// site key and pass it to initializeAppCheck().
const appCheck = initializeAppCheck(app, {
  provider: new ReCaptchaEnterpriseProvider(/* reCAPTCHA Enterprise site key */),
  isTokenAutoRefreshEnabled: true // Set to true to allow auto-refresh.
});

```

## Cloud Firestoreへアクセスしてみる
設定が完了しましたので`Cloud Firestore`にアクセスし、制限ができているかどうかを確認します。

まずアクセスできるかどうかを確認してみます。
```vue
<script setup>
import { doc, setDoc, getFirestore } from "firebase/firestore"
const db = getFirestore()
const postFirestore = () => {
    setDoc(doc(db, "cities", Math.random().toString(32).substring(2)), {
        name: "Los Angeles",
        state: "CA",
        country: "USA",
    })
    console.log('created')
}
</script>

<template>
    <button @click="postFirestore">作成</button>
</template>
```
![app-check2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1046681/1662d18f-e39d-4d37-a24a-0f3ffe541f1a.png)
上記の作成ボタンを押すと`Cloud Firestore`にデータが挿入されていることが確認できます。

![App-Check3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1046681/b6bade11-eead-0ebd-0575-9afd86096b59.png)

次にアクセスが失敗するように設定を変更してみます。
reCAPTCHA Enterpriseのサイトキーを発行した際にドメインを設定しましたが、そちらを削除してみます。
![App-Check4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1046681/320aa7e9-8fd9-8432-9540-0c0da727e081.png)

先ほどのリクエストの際にキャッシュが保存されてしまっているため、そのままリクエストを行うと成功してしまいます。ブラウザをシークレットモードで開いて「作成」ボタンを押してみてください。するとページを開いた時点で既に下記のエラーが発生していますが、「作成」ボタンを押した時にも発生しており、アクセスができません。これで制限ができていることが確認できます。
```tex
chunk-V3SDOGWW.js?v=69717bc6:889 [2024-01-15T13:54:00.095Z]  @firebase/app-check: FirebaseError: AppCheck: ReCAPTCHA error. (appCheck/recaptcha-error).
    at _ReCaptchaEnterpriseProvider.getToken (firebase_app-check.js?v=69717bc6:869:27)
    at async getToken$2 (firebase_app-check.js?v=69717bc6:506:13)
    at async Refresher.operation (firebase_app-check.js?v=69717bc6:600:18)
    at async Refresher.process (firebase_app-check.js?v=69717bc6:100:7)
```

# 終わりに
思ったより簡単に導入できてFirebase便利だな〜と思いました。

# 参考

https://firebase.google.com/docs/app-check?hl=ja

https://firebase.google.com/docs/app-check/web/recaptcha-enterprise-provider?hl=ja

https://zenn.dev/yucatio/articles/5427eede6c3e34

https://firebase.google.com/docs/reference/js/v8/firebase.appcheck.AppCheck