Firebaseを使って一歩一歩確認しながら認証機能（Authentication）とデータベース（Firestore）を使ったサイトを作る

## はじめに

Firebaseチュートリアル的な投稿です。このあたりを網羅します。

- Firebase でHTMLのホスティング
- Firebase Authentication で認証できる
- Firebase Cloud Firetore でデータベースの読み書きができる（認証したユーザーのみ）

サンプルやドキュメントも多くて困らないくらいの情報量があるFirebaseなのですが、なんというかコレ！というサンプルに出会えずあっちこっちすることが多かったので自分好みのサンプル／チュートリアルとして１つまとめておきます。

わたしの場合は、Vue.jsとかReactとかじゃなくて「素のHTMLで出来るかぎりシンプルにやりたい！」です。

> Vue.jsとかのサンプルもそれはそれでとても役に立つのですが、やっぱりまずは素のHTMLで組み込んでみて動きをちゃんと理解しておきたいと思いませんか！？ という性癖のエンジニアなのです

## さっそく一歩一歩手順を見ていきます

可能な限り１つずつ説明していますがもし不明点や間違いなどあったら気軽にコメントください。

### Firebaseの環境を作って最初のページ表示まで

Frebase のサイトにいって、Googleアカウントでログインして、、、（略） ここは色々なページに書いてありますしサイトを見ればわかると思うので早速ですが省略させていただきます。

無事にアカウントが出来たら Firebaseコンソール（Firebaseのサイト） でプロジェクトを追加します。
今回は **get-started** という名前にしました。
進めていくと準備が完了します。

> Google Analytics は OFF にしましたが、ON にしても変わらないです

できあがったら、左のメニューから　**Hosting** を選んで **始める** をクリック。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33095/5f99b6c5-effc-a0e3-e214-c749b663c643.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33095/aac8d54e-d742-b109-6843-3804083ff492.png)

こんな感じで手順が出てくるので手順通りに進めます。
今回は、Firestore や Authentication も使いたいのでチェックボックスにチェックを入れて一緒に手順を見ます。
ページ上にあるコマンドをコンソールで入力していきます。

> Windows なら PowerShell でやることをお勧めします！

```
npm install -g firebase-tools
```

でコマンドラインツールをインストール。 npm 使いますので事前に Node.js のインストールをお願いします。

```
firebase login
```

これで開発環境であるパソコンからFirebaseへのログインしてくれます。 普通に行けばブラウザでGoogleの認証が出てきて認証・許可するとコンソールの方も勝手に進んでくれます。

```
firebase init
```

これで開発環境の準備をするのですが途中でどういう開発環境を作るかの質問がでてきます。

何の機能を使うか聞かれた場合には、下記にチェックを入れて進めます。カーソル上下でスペースでチェックつけてEnterで進みます。

```
(*) Hosting
```

プロジェクトを聞かれるので下記を選び今回作った **get-started** を選んでください。

```
> Use an existing project
```

`firebase init` が無事におわったのでブラウザ側に戻って次に進めます。

`App Nickname` というのを聞かれると思うので適当な名前をいれてください。 これは単に分かりやすい名前を入れればいいだけなので `get-started-nickname` とでもしておきます。

下側の（画像では「リンクして続行」になっているボタン）「登録して続行」というボタンがあるので押します。すると JavaScript のコードが現れます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33095/f4e42d53-8d3d-b51c-ef24-28901995046b.png)

これは後で使うとして、まずはブラウザで出ている手順通り進めてみます。
最後の手順でサーバーへのデプロイをします、

```
firebase deploy
```

デプロイが完了するとページが表示されると思います。 自動的に表示されなかったらコンソール上の最後にURLが出てきますのでそれをコピーしてブラウザで見てみてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33095/746f50c2-9dde-6ead-34f2-7a3d33b807a8.png)

こんなページが出てきたら成功です。これでとりあえず単なるHTML（ファイル的には `public/index.html`）のホスティングが成功しました。

さて次です。

### 認証を入れてみる

今度は Firebaseコンソール から、**Auhtentication** → **ログイン方法を設定** を選びます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33095/4e25351f-a123-2d78-b0b5-5d9ef419fd05.png)

通常ならここで Google とか Github とかを選んでもいいのですが（サンプルアプリでGoogleやGithubアカウント晒したくもない気もするので）今回はサンプルということで メールアドレス/パスワード を選んで有効にしてみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33095/7f21093d-5d2d-877b-43b8-2660e9620b6d.png)

ということで一気にいきます。

完成したHTMLはこちらです。 メールでの認証ができます。 メールでの認証が完了するとまた同じページ（つまり認証）が出てくるという意味のないページですが…… まずは動きを確認してみください。 このコードを `public/index.html` に作って `firebase deploy` してくれたら動きがわかります。

```html:public/index.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Firebase</title>

  <!-- ★Step.1★ Firebase関連のJS呼び出し -->
  <script src="/__/firebase/7.19.0/firebase-app.js"></script>
  <script src="/__/firebase/7.19.0/firebase-auth.js"></script>
  <script src="/__/firebase/7.19.0/firebase-firestore.js"></script>
  <script src="/__/firebase/init.js"></script>

  <!-- ★Step.2★ FirebaseUI呼び出しから認証処理 -->
  <script src="https://cdn.firebase.com/libs/firebaseui/3.5.2/firebaseui.js"></script>
  <link type="text/css" rel="stylesheet" href="https://cdn.firebase.com/libs/firebaseui/3.5.2/firebaseui.css" />
  <script>
    var ui = new firebaseui.auth.AuthUI(firebase.auth());

    var uiConfig = {
      callbacks: {
        signInSuccessWithAuthResult: function(authResult, redirectUrl) {
          return true;
        }
      },
      signInFlow: 'popup',
      signInSuccessUrl: '/index.html', // 認証後に遷移するページ
      signInOptions: [
        firebase.auth.EmailAuthProvider.PROVIDER_ID,
      ],
      tosUrl: '<your-tos-url>', // 本当はTOSのページを作る
      privacyPolicyUrl: '<your-privacy-policy-url>' // 本当はプライバシーポリシーのページを作る
    };

    ui.start('#firebaseui-auth-container', uiConfig);
  </script>

</head>
<body>
  <h1>Hello Firebase🔥</h1>

  <div id="firebaseui-auth-container"></div>
</body>
</html>
```

以下、このコードの説明です。

Step.1 とコメントがあるところでいくつかJSを読みこんでいます。このJSですが、Firebaseコンソール の [設定] → [プロジェクトを設定] を見ると改訂あるコードなのです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33095/d4f68172-7f14-9d7d-f347-5f00fdb28597.png)

すると下の方にこういうコードが出てると思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33095/0e724119-10c5-df56-b8f6-ad6f770eafe4.png)

Firebaseの機能を使うためにこれを HTML に埋め込んでいくことになるのですが 「**TODO: Add SDKs for Firebase products that you want to use https://firebase.google.com/docs/web/setup#available-libraries**」と書いてある通りに今回は「認証（Authentication）」と「データベース（Firestore）」を使いたいので追加が必要になります。

結果、JSの読み込みに関してはこういうコードになります。

```javascript

  <script src="/__/firebase/7.19.0/firebase-app.js"></script>
  <script src="/__/firebase/7.19.0/firebase-auth.js"></script>
  <script src="/__/firebase/7.19.0/firebase-firestore.js"></script>
  <script src="/__/firebase/init.js"></script>
```

次に認証のコードを継ぎ足していきます。

今回は認証画面も自力では作りたくないので FirebaseUI というものに頼ろうと思います。 ということでマニュアルを見ながら作っていきます。色々ややこしいことも書いてあるのですが、自分がやりたいとこだけを取捨選択していってまとめると上で示したコードになります。

https://firebase.google.com/docs/auth/web/firebaseui?hl=ja


ここでいったんデプロイするとこんな感じです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33095/2606b492-21c0-6e47-17ab-343328f0aa20.png)

メアドとパスワードでアカウント登録できます。 登録済みのアカウントなら認証ができます。登録した後にFirebaseコンソールのAuthenticationを見るとアカウントが増えるのがわかりますので色々遊んでみてください。

今は認証がおわったらまた同じページ（index.html）を表示するので無限ログインになるという意味のない状態ですが、とりあえず認証機能は出来ました。

次に、ログイン済みだったらログイン画面ではなく別の表示に切り替えたいと思います。

### ログイン後の処理を作る

さっそくコード全体です。 これを `public/index.html` に作って `firebase deploy` して触ってみてください。

認証画面が出てきて認証をすると、「XXXXでログイン中です」という表示に切り変わります。一回認証してしまうとずっと認証済みになってしまうのでブラウザを閉じるか、シークレットウインドウでやってみてください。

```html:public/index.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Firebase</title>

  <!-- ★Step.1★ Firebase関連のJS呼び出し -->
  <script src="/__/firebase/7.19.0/firebase-app.js"></script>
  <script src="/__/firebase/7.19.0/firebase-auth.js"></script>
  <script src="/__/firebase/7.19.0/firebase-firestore.js"></script>
  <script src="/__/firebase/init.js"></script>

  <!-- ★Step.2★ FirebaseUI呼び出しから認証処理 -->
  <script src="https://cdn.firebase.com/libs/firebaseui/3.5.2/firebaseui.js"></script>
  <link type="text/css" rel="stylesheet" href="https://cdn.firebase.com/libs/firebaseui/3.5.2/firebaseui.css" />
  <script>
    initApp = function() {
      firebase.auth().onAuthStateChanged(function(user) {
        if (user) {
          // ログインしてる時の処理
          document.getElementById('firebaseui-auth-container').textContent = user.email + 'でログイン中です'
        }
        else {
          // ログインしていない時の処理
          firebase.auth().setPersistence(firebase.auth.Auth.Persistence.SESSION);
          var ui = new firebaseui.auth.AuthUI(firebase.auth());

          var uiConfig = {
            callbacks: {
              signInSuccessWithAuthResult: function(authResult, redirectUrl) {
                return true;
              }
            },
            signInFlow: 'popup',
            signInSuccessUrl: '/index.html', // 認証後に遷移するページ
            signInOptions: [
              firebase.auth.EmailAuthProvider.PROVIDER_ID,
            ],
            tosUrl: '<your-tos-url>', // 本当はTOSのページを作る
            privacyPolicyUrl: '<your-privacy-policy-url>' // 本当はプライバシーポリシーのページを作る
          };

          ui.start('#firebaseui-auth-container', uiConfig);
        }
      }, function(error) {
        console.log(error);
      });
    };

    window.addEventListener('load', function() {
      initApp();
    });
  </script>

</head>
<body>
  <h1>Hello Firebase🔥</h1>

  <div id="firebaseui-auth-container"></div>
</body>
</html>
```

書き換えのポイントはココです。

```javascript
      firebase.auth().onAuthStateChanged(function(user) {
        if (user) {
          // ログインしてる時の処理
        }
        else {
          // ログインしていない時の処理
        }
      }
```

コメントの通りですが認証している・していないによって処理を切り分けています。
**ログインしていない時の処理** の時に先ほどのログイン画面を出す処理を書いています。
**ログインしてる時の処理** の方には `document.getElementById('firebaseui-auth-container').textContent = user.email + 'でログイン中です'` という感じでメッセージを出しています。

さてこれでログイン前後の処理の切り替えが出来ました。

次にやっとデータベースとの連携に入ります。

### 認証した情報をつかってデータベースを読み書きする

認証が出来たら次はデータベースの処理に進みます。
Firebaseコンソールから [Cloud Firestore] → [データベースの作成] を選びます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33095/abe40bfd-3763-91c5-510e-bf73e2ef8130.png)

認証してアクセスすることを考えセキュリティルールは本番環境モードを選びます。データベースのロケーションは（今回は速度や高度な機能は使わないので）アジアのどこかあたりを選んでおけばよいです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33095/596f5846-87fe-a614-51a4-3da403e4ce6c.png)

この段階ではコレクション（≒RDBで言うところのテーブル）とかは作らなくて大丈夫です。 いわゆるスキーマーベースのデータベースと違って存在しなかったら勝手にコレクションとかも作ってくれる便利設計です。

とは言えそれでは誰でも触り放題になってしまうのでセキュリティルールだけは作っておきます。

Firebaseコンソールの Firestore の ルール からルールの編集ができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33095/55627855-c970-fc91-15f3-e4e618456858.png)

ルールの編集で下記のようなルールを作っておきます。

```
rules_version = '2';
service cloud.firestore {

  function isAuthUser(auth, userId) {
    return auth != null && auth.uid == userId
  }

  match /databases/{database}/documents {
    match /users/{userId} {
      allow create, write, read, update, delete:  if isAuthUser(request.auth, userId);
    }
  }
}
```

`isAuthUser()` という、**認証済みかつ認証したユーザーIDのチェック** をするという自作関数を作ります。

その後の `match...` という部分で `users` というコレクションで `{userId}` というドキュメントの場合には、`isAuthUser()` がTrueの時のみ `create` `write` `read` `update` `delete` を許します（allow）というルールを定義しておきます。
少しややこしく感じますが、「`{userId}` というドキュメント、つまり、ユーザーIDをキーとしたデータの操作には認証済みの必要があります」ということをルールとして書いてみました。

さて、データベース（Firestore）の準備ができたところで実際のコードに入ります。 今回もまたHTML全体を掲載します。 これを `public/idex.html` に書いてデプロイすると動きが確認できます。
（だいぶコードも長くなってきました…）

動きとしては、ログインすると前回のログイン日時が表示される、というシンプルな動作です。
何回かF5とかで更新すると時刻が変わるのがわかると思います。

あわせてFirebaseコンソール上で Firestore のデータを見ると users コレクションが作られていって日時が変わるのがわかると思います。

```public/index.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Firebase</title>

  <!-- ★Step.1★ Firebase関連のJS呼び出し -->
  <script src="/__/firebase/7.19.0/firebase-app.js"></script>
  <script src="/__/firebase/7.19.0/firebase-auth.js"></script>
  <script src="/__/firebase/7.19.0/firebase-firestore.js"></script>
  <script src="/__/firebase/init.js"></script>

  <!-- ★Step.2★ FirebaseUI呼び出しから認証処理 -->
  <script src="https://cdn.firebase.com/libs/firebaseui/3.5.2/firebaseui.js"></script>
  <link type="text/css" rel="stylesheet" href="https://cdn.firebase.com/libs/firebaseui/3.5.2/firebaseui.css" />
  <script>
    initApp = function() {
      firebase.auth().onAuthStateChanged(function(user) {
        if (user) {
          // ログインしてる時の処理
          document.getElementById('firebaseui-auth-container').textContent = user.email + 'でログイン中です'

          // 前回ログインした日時を取得する
          const db = firebase.firestore();
          db.collection('users').doc(user.uid).get()
          .then(function(doc){

            if(doc.exists){
              document.getElementById('firebaseui-auth-container').textContent += '（前回ログイン日時:' + doc.data().datetime + '）'
            }
            else{
              document.getElementById('firebaseui-auth-container').textContent += '（前回ログイン日時: なし）'
            }

            // 今の日時とログイン時のメールアドレスをFirestoreに書き込む
            db.collection('users').doc(user.uid).set({
              email: user.email,
              datetime: new Date().toLocaleString()
            })
          })
          .catch(function(error) {
            console.log("Error : ", error);
          })
        }
        else {
          // ログインしていない時の処理
          firebase.auth().setPersistence(firebase.auth.Auth.Persistence.SESSION);
          var ui = new firebaseui.auth.AuthUI(firebase.auth());

          var uiConfig = {
            callbacks: {
              signInSuccessWithAuthResult: function(authResult, redirectUrl) {
                return true;
              }
            },
            signInFlow: 'popup',
            signInSuccessUrl: '/index.html', // 認証後に遷移するページ
            signInOptions: [
              firebase.auth.EmailAuthProvider.PROVIDER_ID,
            ],
            tosUrl: '<your-tos-url>', // 本当はTOSのページを作る
            privacyPolicyUrl: '<your-privacy-policy-url>' // 本当はプライバシーポリシーのページを作る
          };

          ui.start('#firebaseui-auth-container', uiConfig);
        }
      }, function(error) {
        console.log(error);
      });
    };

    window.addEventListener('load', function() {
      initApp();
    });
  </script>

</head>
<body>
  <h1>Hello Firebase🔥</h1>

  <div id="firebaseui-auth-container"></div>
</body>
</html>
```

コードの中でポイントだけ解説します。

まず以下の１文で Firestore を使える準備をしています。 特にDB名とかAPI KEYとかも指定していないのですが、最初のJS読み込み時に `/__/firebase/7.19.0/firebase-firestore.js` という今回環境用の設定済みのJSが読まれているので改めてここで設定する必要がないという仕掛けです。

```javascript
          const db = firebase.firestore();
```

次に、**users**コレクション の **userId(user.uid)**ドキュメント のデータの取得を試みます。
`doc.exists` でデータの存在有無が確認できます。データが存在する場合には `doc.data()` でデータの取得が出来ます。

```javascript
          db.collection('users').doc(user.uid).get()
          .then(function(doc){
            if(doc.exists){
　            // データを取得したときの処理を書く doc.data().email などでデータが参照できる
            }
            else{
              // データなかったときの処理を書く
            }
          }
```

次に書きこみの部分のコードについてです。

コードとしてはとってもシンプルで、**users**コレクション の **userId(user.uid)**ドキュメント に `email` `datetime` を書き込んでいます。 `set()` で JSON でデータを渡しているだけです。

```javascript
            db.collection('users').doc(user.uid).set({
              email: user.email,
              datetime: new Date().toLocaleString()
            })
```

全体のソースを追ってもらうととわかると思いますが、ログインしてデータがあったら前回の値を取得して、さらに今回の値を書き込む、という動作をしています。（コールバックが少し入り組んでいるのでわかりづらいかもですが順番に追っていけばわかると思います）

これで認証してのデータベースの読み書きも成功しました。

### さいごに色々試す

色々なメアドで試してみたり、F5で繰り返したり、プライベートウィンドウでアクセスしたり、試してみください。

試しにFirebase管理コンソールで、**Authentication** を見てみてユーザを見てみましょう。ここからユーザーの削除もできるので挙動をみてみると面白いです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33095/e886d943-3154-a3cd-079d-34a78da57692.png)

あとは　**Firestore** を見てどういうデータが入っているかを確認してみましょう。こちらもここからデータが消せたりできるので消してみて再ログインでどういうデータができるかなど確認すると面白いです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33095/79677660-dfcd-cc4d-409e-9a33c12d7297.png)

## まとめ

基本的には、Firebase管理コンソールでポチポチやるのと、index.html しか触ってません。 それでこれだけ作れるのですから Firebase はすごいものです。。

さて、ここまで出来てしまえば後はご自由にです。

- 認証のプロバイダに Google や Github を追加するのも良いです。
- データの書き込みをボタンなどの操作にあわせて行うのも良いです。
- 素のHTMLではなくVue.jsに結合させても良いと思います。

とりあえずここに書いてある基本的なことがわかれば応用ききやすいと思います！

それでは良いFirebaseライフを🔥。