[Firebase 101] サーバ側の実装不要！　Firebaseの認証機能を使ってみる

# TL;DR

Firebaseの認証機能をつかったシンプルなウェブアプリを作って、Firebase上に公開するまでの流れを説明します。

一度使い方をマスターすれば、驚くようなスピードで認証機能を追加できるようになります。
メールアドレスとパスワードでの認証に加え、ソーシャルIDプロバイダと連携した認証方法がわかります。
この記事で作成したサンプルアプリは[**こちら**](https://auth-sample-by-nouris.firebaseapp.com/)から。

この記事を読む前に [**こちらの動画**](https://www.youtube.com/watch?v=-OKrloDzGpU&list=PLl-K7zZEsYLmnJ_FpMOZgyg6XcIGBu2OX&index=19) に目を通しておくとより理解が深まります。

Firebaseをご存知でない方は [**こちら**](https://firebase.google.com/?hl=ja) をご覧ください。





# 読者ターゲット

- フロントエンド開発者でFirebaseに入門してみたい方。
- Firebaseを使った開発のメンタルモデルを作りたい方。
- サーバサイドの実装の手間を省きたい方。
- クラウドの設定が複雑すぎて嫌になってしまいそうな方。
- Firebaseで静的なコンテンツを公開してみたい方。
- **[IMPORTANT]** Firebase初心者の記事でも気にならない寛大な方。





# もくじ

- Firebase Authenticationの簡単な説明
- Firebaseを使った開発の流れ
- 参考文献の紹介
- さらなる話題






# Firebase Authenticationの簡単な説明

- メールアドレスとパスワードによる認証ができます。
- GoogleやFacebookといったソーシャルIDプロバイダとの連携による認証もできます。
- フロントエンドの実装だけでユーザ認証機能が作れます。
- サーバサイドのアプリとも連携させることもできます。






# Firebaseを使った開発の流れ






## 大まかな作業の流れ（３回ほど読んでください）

プロジェクトの準備（Firebase CLIツールのインストール）
↓
アプリケーションのひな型づくり
↓
Firebaseコンソールで新規プロジェクトを作成する
↓
アプリのひな型にFirebaseを組み込む（スニペットの貼り付け）
↓
アプリに認証機能を実装する
↓
Firebaseコンソールで認証機能を有効にする
↓
デプロイ（コマンド一発）







## プロジェクトの準備とコマンドラインツールの導入

```shell
> mkdir myproject
> cd myproject
> npm init
... 

> npm install --save-dev firebase-tools # firebase CLIのインストール

> $(npm bin)/firebase --version
3.18.4

> $(npm bin)/firebase login             # ブラウザでログイン画面が表示されるので、Firebaseにログインしてください。
> $(npm bin)/firebase init              # Firebaseプロジェクトの初期設定を対話形式で行います。

> mkdir public                          # 外部公開用のディレクトリ
> touch public/index.html               # アプリ本体をここに実装していきます）
```

`firebase init`コマンドの詳細は [**この動画**](https://www.youtube.com/watch?v=meofoNuK3vo&index=20&list=PLl-K7zZEsYLmnJ_FpMOZgyg6XcIGBu2OX)をご覧ください（日本語字幕あり）。

別のGoogleアカウントに切り替えたい場合は、`firebase logout` コマンドを実行し、再度ログインしなおしてください。





## アプリケーションのひな型を作成する

```html
<!DOCTYPE html>
<html>
  <body>
    <div id="auth-logged-out">
      <input id="email" type="email">
      <input id="password" type="password">
      <button id="signup">ユーザ登録</button>
      <button id="login">ログイン</button>
      <button id="google-login">Googleログイン</button>
    </div>
    <div id="auth-logged-in" style="display:none;">
      <p>ログインしました。</p>
      <button id="logout">ログアウト</button>
    </div>

    <!-- のちほど **ここにFirebaseのスニペット** を貼り付けます -->

  </body>
</html>
```







## Firebaseコンソールでのプロジェクトを作成する
[Firebaseコンソール](https://console.firebase.google.com)を開き、画面の指示に従って新しいプロジェクトを追加します。

![create-project.png](https://qiita-image-store.s3.amazonaws.com/0/256484/be101aef-0496-a2ff-2f38-5368dbd0f527.png)

プロジェクト名を入力し **プロジェクトを作成** ボタンをクリックします。
![add-project.png](https://qiita-image-store.s3.amazonaws.com/0/256484/2e33a583-d979-a217-e60f-0f2bd130f79c.png)






## プロジェクトとアプリを紐づける

![webapp.png](https://qiita-image-store.s3.amazonaws.com/0/256484/a697f0f5-9741-9a1c-70cf-93e3a8a82bf7.png)

<!--
![snippet.png](https://qiita-image-store.s3.amazonaws.com/0/256484/843c47cf-20b9-ab38-a05c-f44fca89e889.png)
-->






## Firebaseの組み込み

```html
<button id="google-login">Googleログイン</button>

<script src="https://www.gstatic.com/firebasejs/5.0.2/firebase.js"></script>
<script>
  // Initialize Firebase
  var config = {
    apiKey: "",
    authDomain: "",
    databaseURL: "",
    projectId: "",
    storageBucket: "",
    messagingSenderId: ""
  };
  firebase.initializeApp(config);
</script>

</body>
```







## メールアドレスとパスワードによるサインアップ機能を追加する

```javascript
// Firebase認証機能はこの値を通して利用します。
const auth = firebase.auth()

// 登録ボタンのイベント登録
document.querySelector('#signup')
  .addEventListener('click', async ev => {
    try {
      await auth
        .createUserWithEmailAndPassword(...getEmailAndPassword())
    } catch (e) {
      console.log('登録失敗', e)
    }
  })
```





## メールアドレスとパスワードによるログイン機能を追加する
```javascript
// ログインボタンのイベント登録
document.querySelector('#login')
  .addEventListener('click', async ev => {
    try {
      await auth
        .signInWithEmailAndPassword(...getEmailAndPassword())
    } catch(e) {
      console.log('ログイン失敗', e)
    }
  })
```




## Firebaseコンソールでメールアドレスとパスワードによる認証を有効にする

![console-auth.ppng.png](https://qiita-image-store.s3.amazonaws.com/0/256484/cc7c5ab6-a1e4-216b-2161-25ca6692eff2.png)

![auth-mailpass.png](https://qiita-image-store.s3.amazonaws.com/0/256484/dd4b1a60-1067-2863-47f2-a355face80c1.png)






## ログアウト機能を追加する
```javascript
// ログアウトボタンのイベント登録
document.querySelector('#logout')
  .addEventListener('click', () =>
    auth.signOut())
```





### 認証状態を監視する

ユーザがFirebaseからログアウトした場合など、ユーザの認証状態に変化が生じた場合の処理を定義します。
ここでは、ログイン中ならばコンテンツを表示します。
そうでないならば、コンテンツを非表示にしログインフォームを表示します。

```javascript
// 認証状態を監視
auth.onAuthStateChanged(user => {
  if (user) showContents()
  else hideContents()
})
```




## GoogleIDによるログイン機能を追加する

ソーシャルIDプロバイダと連携する場合には、各プロバイダごとに専用に用意された手続をします。
ここでは、Googleにログインするために `GoogleAuthProvider` を使用しています。
`signInWithRedirect` はモバイル用のページと相性が良いです。
OAuthをポップアップウィンドウから利用する場合には、 `signInWithPopup` を利用します。

```javascript
// Googleログインボタンのイベント登録
document.querySelector('#google-login')
  .addEventListener('click', async () => {
    const provider = new firebase.auth.GoogleAuthProvider()
    provider.addScope('profile')
    try {
      await auth.signInWithRedirect(provider)
    } catch(e) {
      console.log('ログイン失敗', e)
    }
  })
```


---



## Firebaseコンソールの認証設定画面でGoogleでログインできるように設定を変更する。

[こちら](#firebaseコンソールでメールアドレスとパスワードによる認証を有効にする)と同様の手続きを行ってください。



---



## Firebase Hostingへデプロイする

次のコマンドを実行するとファイルがFirebase Hostingへデプロイされ、公開用URLが表示されます。

```shell
> $(npm bin)/firebase deploy
```







## ユーザデータをエクスポートする

次のコマンドを実行すると、Firebase上のユーザデータをローカルホストに保存することができます。
ファイルの形式はJSONまたはCSVが選択できます。
`$(npm bin)/firebase auth:export 出力ファイル名 --format=[json|csv]`







# 参考文献の紹介

- [公式ビデオ](https://www.youtube.com/watch?v=k1D0_wFlXgo)
    - まずはこのシリーズを見ましょう。日本語字幕あり。  
      Firebaseを使った開発の流れがわかります。  
      Reactなどのフレームワークとの連携方法などの説明もあります。

- [公式ドキュメント]()
    - ↑のビデオを見たら、公式ドキュメントで認証機能の詳しい説明を見ましょう。  
      データベース機能などFirebaseが提供するさまざまなサービス

- [認証APIリファレンス（ウェブアプリ版）](https://firebase.google.com/docs/reference/js/firebase.auth)







# さらなる話題

- サーバ側のアプリでFirebaseを使う（Firebase Admin SDK）
- データベースを使ってみる
- インスタンス不要！ Cloud Functionsを使ったサーバ側ロジックの実装






# `index.html` の全容
```html
<!DOCTYPE html>
<html>
  <body>
    <div id="auth-logged-out">
      <input id="email" type="email" placeholder="nouris@example.com">
      <input id="password" type="password" placeholder="password">
      <button id="signup">ユーザ登録</button>
      <button id="login">ログイン</button>
      <button id="google-login">Googleログイン</button>
    </div>
    <div id="auth-logged-in" style="display:none;">
      <p>ログインしました。</p>
      <button id="logout">ログアウト</button>
    </div>
    <script src="https://www.gstatic.com/firebasejs/5.0.2/firebase.js"></script>
    <script>
    !function(){

      // Initialize Firebase
      var config = {
        apiKey: "",
        authDomain: "",
        databaseURL: "",
        projectId: "",
        storageBucket: "",
        messagingSenderId: ""
      };
      firebase.initializeApp(config);

      // Firebase認証機能はこの値を通して利用します。
      const auth = firebase.auth()

      // 登録ボタンのイベント登録
      document.querySelector('#signup')
        .addEventListener('click', async ev => {
          try {
            await auth
              .createUserWithEmailAndPassword(...getEmailAndPassword())
          } catch (e) {
            console.log('登録失敗', e)
          }
        })

      // ログインボタンのイベント登録
      document.querySelector('#login')
        .addEventListener('click', async ev => {
          try {
            await auth
              .signInWithEmailAndPassword(...getEmailAndPassword())
          } catch(e) {
            console.log('ログイン失敗', e)
          }
        })

      // Googleログインボタンのイベント登録
      document.querySelector('#google-login')
        .addEventListener('click', async () => {
          const provider = new firebase.auth.GoogleAuthProvider()
          provider.addScope('profile')
          try {
            await auth.signInWithRedirect(provider)
          } catch(e) {
            console.log('ログイン失敗', e)
          }
        })

      // ログアウトボタンのイベント登録
      document.querySelector('#logout')
        .addEventListener('click', () =>
          auth.signOut())

      // 認証状態を監視
      auth.onAuthStateChanged(user => {
        if (user) showContents()
        else hideContents()
      })

      function getEmailAndPassword() {
        const email = document.querySelector('#email').value
        const password = document.querySelector('#password').value
        return [email, password]
      }

      function showContents() {
        document.querySelector('#auth-logged-out').style.display = 'none'
        document.querySelector('#auth-logged-in').style.display = 'block'
      }

      function hideContents() {
        document.querySelector('#auth-logged-out').style.display = 'block'
        document.querySelector('#auth-logged-in').style.display = 'none'
      }

    }() // IIFE
    </script>
  </body>
</html>

```