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

#ログインボタンを作成

```vuejs:signin.vue
<template>
  <div id="app">
    <Header />
    <div class="signin flex">
      <div class="signin-inner flex">
        <h2>ログイン</h2>
        <input type="text" placeholder="Email" v-model="email" />
        <input type="password" placeholder="Password" v-model="password" />
        <button class="btn" @click.prevent="signIn">ログイン</button>
      </div>
    </div>
  </div>
</template>

<script>
import firebase from "firebase";
import Header from "@/components/header.vue";

export default {
  name: "Signin",
  data: function() {
    return {
      email: "",
      password: "",
    };
  },
  components: {
    Header,
  },
  methods: {
    //以下、「メールアドレス」と「パスワード」を使ったサインインの実装。
    signIn: function() {
    },
  },
};
</script>
```
```javascript:signin.vue
data: function() {
    return {
      email: "",
      password: "",
    };
  },
```
上記では、ログインボタンを作成し、「email」「password」に初期値として空配列にしてます。

#routerにログインページをの追加

続いて、router にログインページを追加します。

```javascript:router/index.js
import Vue from "vue";
import VueRouter from "vue-router";
import Main from "../views/main.vue";
import Signin from "../views/signin.vue";
import firebase from "firebase";

Vue.use(VueRouter);

const routes = [
  {
    path: "/",
    name: "Main",
    component: Main,
  },
  {
    path: "/signin",
    name: "Signin",
    component: Signin,
  },

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

export default router;

```

#ログイン機能の追加

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

ログインボタンに、signIn()というclick時のイベントを追加してます。

```vuejs:signin.vue
<template>
  <div id="app">
    <Header />
    <div class="signin flex">
      <div class="signin-inner flex">
        <h2>ログイン</h2>
        <input type="text" placeholder="Email" v-model="email" />
        <input type="password" placeholder="Password" v-model="password" />
        <button class="btn" @click.prevent="signIn">ログイン</button>
      </div>
    </div>
  </div>
</template>

<script>
import firebase from "firebase";
import Header from "@/components/header.vue";

export default {
  name: "Signin",
  data: function() {
    return {
      email: "",
      password: "",
    };
  },
  components: {
    Header,
  },
  methods: {
    //以下、「メールアドレス」と「パスワード」を使ったサインインの実装。
    signIn: function() {
      firebase
        .auth()
        .signInWithEmailAndPassword(this.email, this.password)
        //ユーザーがフォームに入力したら、signInWithEmailAndPassword メソッドを呼び出します。
        .then(
          () => {
            // alert("ログインに成功しました。");
            //上記、登録できたらアラート実行
            this.$router.push("/mypage");
            //ログイン後、上記ルート先へページ遷移
          },
          (err) => {
            alert("ログイン情報が間違っています。", err.message);
            //エラーメッセージ
          }
        );
    },
  },
};
</script>
```

```javascript:signin.vue
signIn: function() {
　　　firebase
        .auth()
        .signInWithEmailAndPassword(this.email, this.password)
        //ユーザーがフォームに入力したら、signInWithEmailAndPassword メソッドを呼び出します。
        .then(
          () => {
            // alert("ログインに成功しました。");
            //上記、登録できたらアラート実行
            this.$router.push("/mypage");
            //ログイン後、上記ルート先へページ遷移
          },
          (err) => {
            alert("ログイン情報が間違っています。", err.message);
            //エラーメッセージ
          }
        );
```

**firebase .auth().signInWithEmailAndPassword(this.email, this.password)**を
追加することで「email」と「password」でログインできようになります。

```javascript:signin.vue
 .then(
          () => {
            // alert("ログインに成功しました。");
            //上記、登録できたらアラート実行
            this.$router.push("/mypage");
            //ログイン後、上記ルート先へページ遷移
          },
          (err) => {
            alert("ログイン情報が間違っています。", err.message);
            //エラーメッセージ
          }
```

上記と追加することでアラートを指定することができます。
また**this.$router.push("/mypage");**を追加することで("/mypage")へページ遷移します。

#新規登録ボタンの追加

次に新規登録機能を作成していきます。

新規登録ボタンに、signUp()というclick時のイベントを追加してます。

```vuejs:signup.vue
<template>
  <div id="app">
    <Header />
    <div class="signup flex">
      <div class="signup-inner flex">
        <h2>新規登録</h2>
        <input type="text" placeholder="Username" v-model="username" />
        <input type="text" placeholder="Email" v-model="email" />
        <input type="password" placeholder="Password" v-model="password" />
        <button class="btn-signup" @click.prevent="signUp">登録</button>
      </div>
    </div>
  </div>
</template>

<script>
import firebase from "firebase";
import Header from "@/components/header.vue";

export default {
  name: "Signup",
  data() {
    return {
      username: "",
      email: "",
      password: "",
    };
  },
  components: {
    Header,
  },
  methods: {
    //以下、「メールアドレス」と「パスワード」を使った新規登録の実装。
    signUp: function() {
      firebase
        .auth()
        .createUserWithEmailAndPassword(this.email, this.password)
        //ユーザーから提供されたメールアドレスとパスワードを検証し、それらを 
         createUserWithEmailAndPassword メソッドに渡す。
        .then(() => {
          firebase
            .auth()
            .currentUser.updateProfile({
              displayName: this.username,
            })
            .then(() => {
              // alert("登録に成功しました。");
              //上記、登録できたらアラート実行
              this.$router.push("/signin");
              //登録後、上記ルート先へページ遷移
            });
        })
        .catch((error) => {
          alert("登録情報が正しくありません。", error.message);
          //エラーメッセージ
        });
    },
  },
};
</script>
```

```javascript:signup.vue
signUp: function() {
　　firebase
        .auth()
        .createUserWithEmailAndPassword(this.email, this.password)
        //ユーザーから提供されたメールアドレスとパスワードを検証し、それらを 
         createUserWithEmailAndPassword メソッドに渡す。
        .then(() => {
          firebase
            .auth()
            .currentUser.updateProfile({
              displayName: this.username,
            })
            .then(() => {
              // alert("登録に成功しました。");
              //上記、登録できたらアラート実行
              this.$router.push("/signin");
              //登録後、上記ルート先へページ遷移
            });
        })
        .catch((error) => {
          alert("登録情報が正しくありません。", error.message);
          //エラーメッセージ
        });
    },
```

**firebase .auth().createUserWithEmailAndPassword(this.email, this.password)**を
追加することで「email」と「password」で登録できようになります。

#routerに新規登録ページをの追加

内容はログインページと同様追加するだけなので割愛します。

#ログアウトボタンの追加

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

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

```vuejs:header.vue
<template>
  <header class="header">
    <router-link to="/" class="header-ttl">
      <span class="header-ttl-color">C</span>inemary</router-link
    >
    <ul class="header-menu">
      <li>
        <router-link to="/signup" class="header-link" v-if="!authenticatedUser"
          >新規登録</router-link
        >
      </li>
      <li>
        <router-link to="/signin" class="header-link" v-if="!authenticatedUser"
          >ログイン</router-link
        >
      </li>
      <li class="dropdown-items" v-if="authenticatedUser">
       <button class="dropdown-link" @click="signOut" v-if="authenticatedUser">
         ログアウト</button>
      </li>
    </ul>
  </header>
</template>

<script>
import firebase from "firebase";

export default {
  name: "signOut",
  data() {
    return {
      authenticatedUser: "",
      isOpen: false,
    };
  },
  methods: {
    //ログアウトを実装
    signOut: function() {
      firebase
        .auth()
        .signOut()
        .then(() => {
          // alert("ログアウトしました。");
          this.$router.push("/signin");
        })
        .catch(() => {
          // alert("ログアウトができません。");
        });
    },
</script>
```

```javascript:header.vue
signOut: function() {
      firebase
        .auth()
        .signOut()
        .then(() => {
          // alert("ログアウトしました。");
          this.$router.push("/signin");
        })
        .catch(() => {
          // alert("ログアウトができません。");
        });
```

**firebase .auth().signOut()**を追加するだけでログアウトを実装できます。

#ユーザーが認証状況によって、ヘッダーの状態を変更

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


```vuejs:header.vue
<template>
  <header class="header">
    <router-link to="/" class="header-ttl">
      <span class="header-ttl-color">C</span>inemary</router-link
    >
    <ul class="header-menu">
      <li>
        <router-link to="/signup" class="header-link" v-if="!authenticatedUser"
          >新規登録</router-link
        >
      </li>
      <li>
        <router-link to="/signin" class="header-link" v-if="!authenticatedUser"
          >ログイン</router-link
        >
      </li>
      <li>
        <span
          class="dropdown-menu"
          v-click-outside="clickoutside"
          @click="toggle"
        >
          <a class="header-link"
            >マイページ<span class="dropdown-arrow"></span
          ></a>
          <ul class="dropdown" :class="{ isOpen }">
            <li class="dropdown-items">
              <router-link to="/mypage" class="dropdown-link"
                >マイページ</router-link
              >
            </li>
            <li class="dropdown-items">
              <router-link to="/profile" class="dropdown-link"
                >プロフィール編集</router-link
              >
            </li>
            <li class="dropdown-items" v-if="authenticatedUser">
              <button
                class="dropdown-link"
                @click="signOut"
                v-if="authenticatedUser"
              >
                ログアウト
              </button>
            </li>
          </ul>
        </span>
      </li>
    </ul>
  </header>
</template>

<script>
import firebase from "firebase";

export default {
  name: "signOut",
  data() {
    return {
      authenticatedUser: "",
      isOpen: false,
    };
  },
  methods: {
    //ログアウトを実装
    signOut: function() {
      firebase
        .auth()
        .signOut()
        .then(() => {
          // alert("ログアウトしました。");
          this.$router.push("/signin");
        })
        .catch(() => {
          // alert("ログアウトができません。");
        });
    },
  mounted() {
    //以下、ユーザーが認証済みであれば「ログアウト」を表示
    firebase.auth().onAuthStateChanged((user) => {
      if (user) {
        this.authenticatedUser = true;
      } else {
        this.authenticatedUser = false;
      }
    });
};
</script>
```

```javascript:header.vue
(省略)

    return {
      authenticatedUser: "",
      isOpen: false,
    };
  },
  methods: {
    toggle: function() {
      this.isOpen = !this.isOpen;
      //「!」を先頭につける事で真偽が逆。
    },

(省略)

    //以下、ユーザーが認証済みであれば「ログアウト」を表示
    firebase.auth().onAuthStateChanged((user) => {
      if (user) {
        this.authenticatedUser = true;
      } else {
        this.authenticatedUser = false;
      }
    });
```

**firebase.auth()onAuthStateChanged()**を追加することでログイン中のみ、
ログアウトボタンが表示されます。逆に、ログインしていない状態だと、ログインボタンが表示されます。


#ユーザーが認証状況によって、アクセスできるページを制限

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

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

// 追加
import firebase from "firebase";

(省略)

// 追加
  {
    path: "/",
    name: "Main",
    component: Main,
  },
  {
    path: "/signin",
    name: "Signin",
    component: Signin,
  },
  {
    path: "/signup",
    name: "Signup",
    component: Signup,
  {
    path: "/",
    name: "Main",
    component: Main,
  },
  {
    path: "/signin",
    name: "Signin",
    component: Signin,
  },
  {
    path: "/signup",
    name: "Signup",
    component: Signup,
  },
  {
    path: "/mypage",
    name: "Mypage",
    component: Mypage,
    meta: { requiresAuth: true },
    //このルートは認証が必要であることの状態をもたせている。
  },

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

router.beforeEach((to, from, next) => {
  //ページ遷移が起こる直前にその関数が実行。
  //引数のto,fromには，現在遷移しようとしているルーティングの遷移先ルートと遷移元ルートの情報が入ってる。
  const requiresAuth = to.matched.some((record) => record.meta.requiresAuth);
  //requiresAuthの状態をもつルートレコードなのかを確認。
  if (requiresAuth) {
    // このルートはログインされているかどうか認証が必要。
    // もしされていないならば、ログインページにリダイレクトします。
    firebase.auth().onAuthStateChanged(function(user) {
      if (user) {
        next();
      } else {
        next({
          path: "/signin",
          query: { redirect: to.fullPath },
        });
      }
    });
  } else {
    next();
    //同じ階層にある要素を取得できるのですが、対象要素の次に配置されている要素だけを取得する
  }
});
```

```javascript:router/index.js
  {
    path: "/mypage",
    name: "Mypage",
    component: Mypage,
    meta: { requiresAuth: true },
    //このルートは認証が必要であることの状態をもたせている。
  },

(省略)

router.beforeEach((to, from, next) => {
  //ページ遷移が起こる直前にその関数が実行。
  //引数のto,fromには，現在遷移しようとしているルーティングの遷移先ルートと遷移元ルートの情報が入ってる。
  const requiresAuth = to.matched.some((record) => record.meta.requiresAuth);
  //requiresAuthの状態をもつルートレコードなのかを確認。
  if (requiresAuth) {
    // このルートはログインされているかどうか認証が必要。
    // もしされていないならば、ログインページにリダイレクトします。
    firebase.auth().onAuthStateChanged(function(user) {
      if (user) {
        next();
      } else {
        next({
          path: "/signin",
          query: { redirect: to.fullPath },
        });
      }
    });
  } else {
    next();
    //同じ階層にある要素を取得できるのですが、対象要素の次に配置されている要素だけを取得する
  }
});
```
上記を追記することで、「requiresAuth」の状態をもつルートレコードか確認。
ログインされているかどうか認証が確認した後、もしされていないならば、ログインページにリダイレクトします。
ログイン状態であれば、そのままアクセス元にページ遷移します。