Nuxt.jsとFirebaseAuthentication/FirebaseUIでログイン認証

福岡から世界中の"むずかしい"を簡単にする株式会社[diffeasy](https://diffeasy.com)CTOの西[@_takeshi_24](https://twitter.com/_takeshi_24)です。

この記事はアドベントカレンダー「[diffeasyCTO西の24(にし)日連続投稿チャレンジ Advent Calendar 2019](https://qiita.com/advent-calendar/2019/diffeasy24)」の3日目の記事です。

この記事は「Nuxt.jsとFirebaseとCloudFunctionsでWebアプリ開発」シリーズとして、連載していきます。
Nuxt.jsとFirebaseなどを使ってWebアプリケーション開発にチャレンジしたい方、是非Qiitaアカウントか[twitterをフォロー](https://twitter.com/_takeshi_24)していただき、ツッコミやいいね！お願いします！


## はじめに
Nuxt.jsとFireabseの準備がまだの方は、こちらの記事を参考に準備をお願いします！
[Nuxt.jsとFirebaseとCloudFunctionsでWebアプリ開発 - 準備編](https://qiita.com/_takeshi_24/items/c26e4a664e27b652d9a6)

連載2回目の今回は、FirebaseAuthenticationを使って、むずかしい認証の仕組みを簡単に構築する手順について説明します！

## Firebase Authenticationで認証
[Firebase Authentication](https://firebase.google.com/docs/auth/?hl=ja)はFirebaseで提供されるサービスの１つで、バックエンドの開発なしに

- メールアドレス/パスワード認証
- 電話番号認証
- SNS（Google、Facebook、Twitter）認証

など、ほとんどのアプリケーションで必要となる認証機能を組み込むことができます。

### Firebase Authenticationの準備
1.Firebase対象プロジェクトのコンソール画面を開き、サイドメニューの「Authentication」を開きます。
2.「ログイン方法を設定」をクリックします。
![ログイン方法.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3003/e0dc5dfb-c462-d8c0-f9b8-fb2894126e44.png)

3.ログイン方法を選択します。今回はメールアドレス/パスワード認証を選択します。
![メールアドレスログイン.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3003/423b53c8-7e06-2b07-63ff-74290db48c2d.png)

### Nuxt.jsにFirebaseとFirebaseUIのインストール
FirebaseUIを利用すると、ログインの画面も自動で作成可能です。

1.前回[Nuxt.jsとFirebaseとCloudFunctionsでWebアプリ開発 1 - 準備編]()で作成したnuxt-sampleディレクトリで、FirebaseとFirebaseUIをインストールします。

```terminal
$ yarn add firebase firebaseui
```

2.Nuxt.jsでFirebaseに接続するための認証情報などを環境変数に定義します。ここではcross-envを使って、環境を切り替えるので、cross-envをインストールします。

```terminal
$ yarn add cross-env
```

3.開発用のenv.development.js、本番用のenv.production.jsを作成します。

- FirebaseコンソールのProjectOverview横のプロジェクト設定を開きます。
- 画面下の方にあるマイアプリの「sample」内にある「Firebase SDK snippet」「構成」を開きます。

※ここでは一旦内容は同じですが、次回の記事で環境ごとに違った設定を利用します。

```javascript:/env.development.js
module.exports = {
  BASE_URL: "http://localhost:3000",
  FB_API_KEY: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  FB_AUTH_DOMAIN: "sample-xxxxx.firebaseapp.com",
  FB_DATABASE_URL: "https://sample-xxxxx.firebaseio.com",
  FB_PROJECTID: "sample-xxxxx",
  FB_STORAGE_BUCKET: "sample-xxxxx.appspot.com",
  FB_MESSAGING_SENDER_ID: "xxxxxxxxxxxxxx",
  FB_APP_ID: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  FB_MEASUREMENT_ID: "xxxxxxxxxxx",
};
```

```javascript:/env.production.js
module.exports = {
  BASE_URL: "http://localhost:3000",
  FB_API_KEY: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  FB_AUTH_DOMAIN: "sample-xxxxx.firebaseapp.com",
  FB_DATABASE_URL: "https://sample-xxxxx.firebaseio.com",
  FB_PROJECTID: "sample-xxxxx",
  FB_STORAGE_BUCKET: "sample-xxxxx.appspot.com",
  FB_MESSAGING_SENDER_ID: "xxxxxxxxxxxxxx",
  FB_APP_ID: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  FB_MEASUREMENT_ID: "xxxxxxxxxxx"
};
```

**※gitでソース管理している場合は、```.gitignore```に```env.*.js```を追加して、ソース管理から外しておきましょう！**

4.package.jsonに本番用のgenerateスクリプトを追加します。

```json:/package.json
  "scripts": {

    "generate": "nuxt generate",
    "generate:prod": "cross-env NODE_ENV=\"production\" nuxt generate",

  }
```

5.nuxt.config.jsに環境をセットする処理を追加します。

```javascript:/nuxt.config.js
const environment = process.env.NODE_ENV || "development";
const envSet = require(`./env.${environment}.js`);
export default {
  env: envSet

}
```

6.pluginsにfirebase.jsを作成します。

```javascript:/plugins/firebase.js
import firebase from 'firebase'
import 'firebase/auth'
import 'firebase/messaging'

const config = {
  apiKey: process.env.FB_API_KEY,
  authDomain: process.env.FB_AUTH_DOMAIN,
  databaseURL: process.env.FB_DATABASE_URL,
  projectId: process.env.FB_PROJECTID,
  storageBucket: process.env.FB_STORAGE_BUCKET,
  messagingSenderId: process.env.FB_MESSAGING_SENDER_ID,
  appId: process.env.FB_APP_ID,
  timestampsInSnapshots: true
}
if (!firebase.apps.length) {
  firebase.initializeApp(config)
}

export const authProviders = {
  Email: firebase.auth.EmailAuthProvider.PROVIDER_ID
}

export const auth = firebase.auth()
```

7.storeに認証用のauthストアを作成します。

```javascript:/store/index.js
import "firebase/firestore";

export const strict = false;

export const state = () => ({});
export const getters = {};
export const mutations = {};
export const actions = {};
```

```javascript:/store/auth.js
import { auth } from "~/plugins/firebase";

export const state = () => ({
  status: "",
  token: localStorage.getItem("token") || "",
  username: "",
  idToken: {}
});

export const getters = {
  isLoggedIn: state => state.status === "loggedIn"
};

export const actions = {
  getUser({ commit }, user) {
    commit("setUser", user);
  },
  logout({ commit }) {
    auth.signOut().then(() => {
      commit("logout");
    });
  }
};

export const mutations = {
  async setUser(state, user) {
    state.status = "loggedIn";
    state.username = user.displayName;
    state.idToken = await user.getIdToken(true);
  },
  logout(state) {
    state.status = "loggedOut";
    state.username = "";
  }
};
```
8.FirebaseUIの認証画面コンポーネントを作成します。

```vuejs:/components/FirebaseAuth.vue
<template>
  <div id="firebaseui-auth-container"></div>
</template>

<script>
import { auth, authProviders } from "~/plugins/firebase";

export default {
  name: "FirebaseAuth",
  mounted() {
    auth.onAuthStateChanged(user => {
      if (!user) {
        const firebaseui = require("firebaseui");
        const ui =
          firebaseui.auth.AuthUI.getInstance() ||
          new firebaseui.auth.AuthUI(auth);

        const config = {
          signInOptions: [authProviders.Email, authProviders.Facebook],
          callbacks: {
            signInSuccessWithAuthResult: authResult => {
              window.location.href = "/";
              return false;
            }
          },
          signInSuccessUrl: "/",
          signInFlow: "popup"
        };

        ui.start("#firebaseui-auth-container", config);
      } else {
        this.$router.push("/");
      }
    });
  }
};
</script>
```
9.ログインページを作成し、先ほどのコンポーネントを読み込みます。

```vuejs:/pages/login.vue
<template>
  <firebase-auth />
</template>

<script>
import FirebaseAuth from "@/components/FirebaseAuth";

export default {
  name: "Login",
  components: {
    FirebaseAuth
  }
};
</script>

```

10.ページ遷移時にログイン状態をチェックするための処理をmiddlewareに追加します。

```javascript:/middleware/auth.js
import { auth } from "~/plugins/firebase";

export default function({ route, store, redirect }) {
  auth.onAuthStateChanged(user => {
    if (user) {
      store.dispatch("auth/getUser", user);
    } else if (route.name !== "login") {
      redirect("/login");
    }
  });
}
```


11.nuxt.config.jsに以下の内容を追加します。

```javascript:/nuxt.config.js
  /*
   ** Global CSS
   */
  css: ["firebaseui/dist/firebaseui.css"],
  /*
   ** Plugins to load before mounting the App
   */
  plugins: ["@/plugins/firebase"],
  // 画面遷移時にFirebase認証処理を実行
  router: {
    middleware: ["auth"]
  },

```

12.Nuxt.jsを起動します。

```terminal
$ yarn run dev
```
**※core-jsのエラーが出る場合は以下のパッケージをインストール**

```terminal
$ yarn add core-js@2
```

13.http://localhost:3000 にアクセスすると、/loginに飛ばされます。
![FirebaseUIAuthentication.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3003/94cc13c6-fa72-bd47-07ca-cc6de23c48ec.png)

14.メールアドレスを入力して、アカウントが存在しなければ、アカウント作成画面に遷移します。
![FirebaseUIAuthenticationアカウント作成.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3003/d7096aad-c3a2-8eb4-a015-5a3ae4d736a3.png)

15.アカウントを作成すると、認証が完了し、トップ画面に遷移します。Firebaseコンソールで、Authenticationを開くと、アカウントが登録されていることを確認できます。
<img width="942" alt="FirebaseUIAuthenticationコンソール.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3003/56cf3c6a-af7d-5afb-6f37-8f150ec3c180.png">

##まとめ
FirebaseAuthenticationを使って、ログイン処理、ユーザー登録、セッション管理などのむずかしい処理を簡単にできました！

この記事は「Nuxt.jsとFirebaseとCloudFunctionsでWebアプリ開発」シリーズとして、連載していきます。
続きはアドベントカレンダー「[diffeasyCTO西の24(にし)日連続投稿チャレンジ Advent Calendar 2019](https://qiita.com/advent-calendar/2019/diffeasy24)」に掲載していきます。

Nuxt.jsとFirebaseなどを使ってWebアプリケーション開発にチャレンジしたい方、是非Qiitaアカウントか[twitterをフォロー](https://twitter.com/_takeshi_24)していただき、ツッコミやいいね！お願いします！

[#advent_24](https://twitter.com/hashtag/advent_24)のハッシュタグでフィードバックいただけると嬉しいです！

##参考サイト
- [Nuxt/VuexでFirebase Authenticationを使ってユーザー認証機能を作る](https://blog.ikedaosushi.com/entry/2019/04/17/201246)

- [Nuxtでcross-envを使い環境ごとに環境変数を分ける](https://qiita.com/TakahiRoyte/items/c152ad8baa191ed1f8ae)