【Vue × Firebase】認証機能を作成【Vuex】

#firebase の認証機能を作成
次に、firebase の認証機能を作成しましょう。

まず最初にログイン用のページを作成します。

以下のように、Login.vueを作成してください。

```vuejs:Login.vue
<template>
  <div class="login">
    <h2 class="login__ttl">ログイン</h2>
    <button class="login__button">Googleアカウントでログイン</button>
  </div>
</template>

<script>
  export default {};
</script>

<style lang="scss" scoped>
  .login {
    padding-top: 3rem;
    text-align: center;
    &__button {
      background-color: #4180ed;
      color: #fff;
      margin-top: 3rem;
      padding: 0.5rem 1rem;
      border-radius: 0.25rem;
      border: none;
      cursor: pointer;
      outline: none;
      appearance: none;
    }
  }
</style>
```
続いて、router にログインページを追加します。

```javascript:router/index.js
import Vue from "vue";
import VueRouter from "vue-router";
import Bord from "../views/Bord.vue";
import Login from "../views/Login.vue"; // 追加

Vue.use(VueRouter);

const routes = [
  {
    path: "/",
    name: "Bord",
    component: Bord,
  },
  {
    path: "/about",
    name: "About",
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () =>
      import(/* webpackChunkName: "about" */ "../views/About.vue"),
  },
  // 追加
  {
    path: "/login",
    name: "Login",
    component: Login,
  },
];

const router = new VueRouter({
  mode: "history",
  base: process.env.BASE_URL,
  routes,
});

export default router;
```

次にログイン機能を作成していきます。

ログインボタンに、click 時のイベントを追加し、実行するメソッドで、アクションの login を呼び出しています。

```vuejs:Login.vue
<template>
  <div class="login">
    <h2 class="login__ttl">ログイン</h2>
    <!-- 変更 -->
    <button class="login__button" @click="login">
      Googleアカウントでログイン
    </button>
  </div>
</template>

<script>
  export default {
    // 追加
    methods: {
      login() {
        this.$store.dispatch("login");
      },
    },
  };
</script>
```

続いて、login アクションを作成しましょう。

```javascript:store/index.js
(省略)

import firebase from 'firebase' // 追加
import router from '../router' // 追加
(省略)

  actions: {
    // 追加
    login() {
      const provider = new firebase.auth.GoogleAuthProvider();
      firebase.auth().signInWithPopup(provider).then(() => {
        router.push('/')
      })
    }

(省略)
```

import firebase from 'firebase'で firebase をインポートし、login アクションでは、firebase の認証のメソッドを呼び出しています。

ここまでできたら、ログインページでログインを選択すると、google のアカウントでログインできるようになっているかと思います。

/loginにアクセスして試してみましょう。

続いて、ログアウト機能を実装していきます。

ログアウトボタンを Header コンポーネントに追加しましょう。

```vuejs:Header.vue
<template>
  <header class="header">
    <h1 class="header__ttl">タスク管理アプリ</h1>
    <div class="header__menu">
      <router-link to="/" class="header__link">Home</router-link>
      <router-link to="/about" class="header__link">About</router-link>
      <button class="header__link" @click="logout">Logout</button>
    </div>
  </header>
</template>

<script>
  export default {
    methods: {
      logout() {
        this.$store.dispatch("logout");
      },
    },
  };
</script>
```

ログインボタンと同様に、logout アクションを呼び出しています。

続いて、logout アクションを作成しましょう。

```javascript:store/index.js
(省略)

actions: {}
  // 追加
  logout() {
    firebase.auth().signOut()
  },

  (省略)
```

次に、ログイン中のユーザー情報を取得します。

store にユーザー情報は格納したいので、まずは state に loginUser を追加します。

```javascript:store/index.js
(省略)

  state: {
    loginUser: null, // 追加
    lists: [{
        title: "リスト1",
        cards: ["カード１", "カード２", "カード３"]
      },
      {
        title: "リスト2",
        cards: ["カード１", "カード２"]
      },
      {
        title: "リスト3",
        cards: []
      },
    ]
  },

(省略)
```

次に、loginUser を保存するための、アクションとミューテーションを追加します。

```javascript:store/index.js
(省略)
  mutations: {
    // 追加
    setLoginUser(state, user) {
      state.loginUser = user
    },

(省略)

  actions: {
    // 追加
    setLoginUser({commit}, user) {
      commit('setLoginUser', user)
    },

(省略)
```

続いて、ログアウト時に、ユーザー情報を初期化するアクションとミューテーションを追加します。

```javascript:store/index.js
(省略)

  mutations: {
    // 追加
    deleteLoginUser(state) {
      state.loginUser = null
    },

(省略)

  actions: {
    // 追加
    deleteLoginUser({commit}) {
      commit('deleteLoginUser')
    },
```

続いて、App.vue に、firebase からログインユーザーを取得する処理を追加します。 ログインしている状態だと、setLoginUserを呼び出し、ログインしていない状態だと、deleteLoginUserを呼び出しています。

```vuejs:App.vue
<template>
  <div id="app">
    <Header />
    <router-view />
  </div>
</template>

<script>
  import firebase from "firebase"; // 追加
  import Header from "@/components/Header";

  export default {
    components: {
      Header,
    },
    // 追加
    created() {
      firebase.auth().onAuthStateChanged((user) => {
        if (user) {
          this.$store.dispatch("setLoginUser", user);
        } else {
          this.$store.dispatch("deleteLoginUser");
        }
      });
    },
  };
</script>

(省略)
```

続いて、ログインの状態に応じて、ヘッダーの状態を変えていきましょう。

```vuejs:Header.vue
<template>
  <header class="header">
    <h1 class="header__ttl">タスク管理アプリ</h1>
    <div class="header__menu">
      <router-link to="/" class="header__link">Home</router-link>
      <router-link to="/about" class="header__link">About</router-link>
      <!-- 変更 -->
      <button
        v-if="$store.state.loginUser"
        class="header__link"
        @click="logout"
      >
        Logout
      </button>
      <!-- 追加 -->
      <router-link v-else to="/login" class="header__link">Login</router-link>
    </div>
  </header>
</template>
```

v-if="$store.state.loginUser"とすることによって、ログイン中のみ、ログアウトボタンが表示されます。逆に、ログインしていない状態だと、ログインボタンが表示されます。

続いて、ログインの状態によって、アクセスできるページを制限しましょう。 未ログインの状態では、ログインページにしか移動できなくし、ログイン状態では、どのページにもアクセスできるようにします。


```javascript:router/index.js

(省略)

// 追加
import firebase from "firebase";

(省略)

// 追加
router.beforeResolve((to, from, next) => {
  if (to.path == "/login") {
    next();
  } else {
    firebase.auth().onAuthStateChanged((user) => {
      if (user) {
        next();
      } else {
        next({ path: "/login" });
      }
    });
  }
});
export default router;
```

beforeResolveは、ナビゲーションガードといい、ページ遷移中に何かしらの処理を挟むことができます。

ここでは、アクセス先が、/loginの場合はそのまま通し、そうじゃない場合は、ログイン中のみ通して、未ログイン中だと、/loginにリダイレクトします。

以上で認証機能の作成は完了しました。