Help us understand the problem. What is going on with this article?

Nuxt.js + Firebase + Netlifyでログイン認証+push通知機能のあるPWAを作る(その2) - ログイン処理、ユーザ登録、認証処理実装

More than 1 year has passed since last update.

第1回と第3回の記事はこちらになります
- 第1回
Nuxt.js + Firebase + Netlifyでログイン認証+Push通知機能のあるPWAを作る(その1) - 環境構築(Nuxt、Firebase設定、Netlifyホスティング、自動デプロイ) - Qiita
- 第3回
Nuxt.js + Firebase + Netlifyでログイン認証+push通知機能のあるPWAを作る(その3) - Push通知受け取り&Functionsを使ってRealtimeDatabaseにトークンを保存 - Qiita

この回ではログイン画面、ユーザ登録画面からFirebase Authenticateを使用してログイン機能、ユーザ登録機能を実装していきます。
UIフレームワークはBootstrap Vueを使用します。(プロジェクト作成時に設定済み)

Vuexの実装

ログインしたユーザ情報をVuexのStoreに保存するためにaction,mutation,stateをStoreに定義します。
(Vuexの説明については割愛)

Firebaseのログイン、ログアウト、ユーザ登録処理

まずはFirebaseのログイン、ログアウト、ユーザ登録の実装を行います。
それぞれをメソッドとして定義します。

store/user.js

import firebase from '@/plugins/firebase'

const firebaseLogin = (user) => {
  return new Promise((resolve, reject) => {
    firebase
      .auth()
      .signInWithEmailAndPassword(user.email, user.password)
      .then((data) => {
        const { displayName, email } = data.user
        resolve({ displayName, email })
      })
      .catch((error) => {
        reject(error)
      })
  })
}

const firebaseLogout = (user) => {
  return new Promise((resolve, reject) => {
    firebase
      .auth()
      .signOut()
      .then(() => {
        resolve()
      })
      .catch((error) => {
        reject(error)
      })
  })
}

const firebaseCreateUser = (user) => {
  return new Promise((resolve, reject) => {
    firebase
      .auth()
      .createUserWithEmailAndPassword(user.email, user.password)
      .then((data) => {
        resolve(data)
      })
      .catch((error) => {
        reject(error)
      })
  })
}

state

保存先としてuserDataを定義。

store/user.js

export const state = () => ({
  userData: null
})

getter

stateを参照するgetterを定義。
stateにユーザ情報があるかの判定を行うisAuthenticatedとstateのユーザ情報を取得するgetUserDataを定義します。

store/user.js

export const getters = {
  isAuthenticated(state) {
    return !!state.userData
  },
  getUserData(state) {
    return state.userData
  }
}

mutation

mutationにはユーザデータをstateにセットする処理を定義。

store/user.js

export const mutations = {
  setUser(state, payload) {
    state.userData = payload
  }
}

action

ログイン、ログアウト、ユーザ登録、ユーザ情報セットのactionの定義を行います。
ユーザ登録に関してはユーザ登録が完了したタイミングでログインも行うようにします。

store/user.js

export const actions = {
  async login({ commit }, user) {
    try {
      const loginResult = await firebaseLogin(user)
      commit('setUser', loginResult)
      return
    } catch (e) {
      throw new Error(e)
    }
  },
  async logout({ commit }) {
    try {
      await firebaseLogout()
      return
    } catch (e) {
      throw new Error(e)
    }
  },
  async createUser({ commit }, user) {
    try {
      const createLogin = await firebaseCreateUser(user)
      const resultLogin = await firebaseLogin(user)
      commit('setUser', null)
      return resultLogin
    } catch (e) {
      throw new Error(e)
    }
  },
  setUserData({ commit }, user) {
    commit('setUser', user)
  }
}

ログイン画面

画面デザイン

画面構成は以下のようにします

  • 「LOGIN」のタイトル
  • メールアドレス入力欄
  • パスワード入力欄
  • ログインボタン
  • ユーザ登録画面遷移リンク

pages/login.vue

<template>
  <div>
    <b-container fluid>
      <b-row class="text-center" align-v="center">
        <b-col cols="1" md="5" />
        <b-col cols="10" sm="10" md="2">
          <h1 style="margin-top: 60px;">LOGIN</h1>
          <b-form style="margin-top: 30px;" @submit="signIn">
            <b-form-group label="Email address" label-for="input-1">
              <b-form-input
                id="input-1"
                type="email"
                required
                placeholder="Enter email"
                v-model="email"
              ></b-form-input>
            </b-form-group>
            <b-form-group label="Password" label-for="input-2">
              <b-form-input
                id="input-2"
                type="password"
                required
                placeholder="Enter password"
                v-model="password"
              ></b-form-input>
            </b-form-group>
            <b-form-group style="margin-top: 30px;">
              <b-button block type="submit" variant="success">
                Sign In
              </b-button>
            </b-form-group>
          </b-form>
          <nuxt-link to="/createAccount">create account</nuxt-link>
        </b-col>
        <b-col cols="1" />
      </b-row>
    </b-container>
  </div>
</template>

<script>
export default {
  data() {
    return {
      email: '',
      password: ''
    }
  },
  methods: {
    signIn(event) {
      event.preventDefault()
      console.log('sign in.')
    }
  }
}
</script>

スクリーンショット 2019-07-30 10.21.53.png

ログイン処理実装

VuexのアクションからFirebaseログイン→トップに遷移の実装を行います。
actionのhelperをインポート。

import { mapActions } from 'vuex'

methods内にaction helperを用いて使用アクションの定義を行います。

...mapActions('user', ['login'])

signInメソッド内にactoinのloginを叩いてログインを行い、正常に処理が通ればトップに遷移する処理を実装します。

signIn(event) {
  event.preventDefault()
  const user = {
    email: this.email,
    password: this.password
  }
  this.login(user)
    .then(() => {
      this.$router.push('/')
    })
    .catch((error) => {
      alert(error)
    })
}

ここまで実装できたらFirebaseコンソールのAuthenticationの画面を開き、ユーザの追加というボタンがあるので選択し、メールアドレスとパスワードを入力しユーザを追加します。

追加したユーザのメールアドレスとパスワードを使って実装したログイン画面からログインが可能になっています。

ユーザ登録画面

画面デザイン

画面構成は以下のようにします

  • 「Create Account」のタイトル
  • メールアドレス入力欄
  • パスワード入力欄
  • パスワード確認入力欄
  • サインアップボタン

pages/createAccount.vue

<template>
  <div class="loginbackgroud">
    <b-row class="text-center" align-v="center">
      <b-col cols="1" md="4" />
      <b-col cols="10" sm="10" md="4">
        <h1 style="margin-top: 60px;">Create Account</h1>
      </b-col>
    </b-row>
    <b-container fluid>
      <b-row class="text-center" align-v="center">
        <b-col cols="1" md="5" />
        <b-col cols="10" sm="10" md="2">
          <b-form style="margin-top: 30px;" @submit="signUp">
            <b-form-group label="Email address" label-for="input-1">
              <b-form-input
                id="input-1"
                type="email"
                required
                placeholder="Enter email"
                v-model="email"
              ></b-form-input>
            </b-form-group>
            <b-form-group label="Password" label-for="input-2">
              <b-form-input
                id="input-2"
                type="password"
                required
                placeholder="Enter password"
                v-model="password"
              ></b-form-input>
            </b-form-group>
            <b-form-group label="ConfirmPassword" label-for="input-2">
              <b-form-input
                id="input-2"
                type="password"
                required
                placeholder="Enter confirm password"
                v-model="confirmPassword"
              ></b-form-input>
            </b-form-group>
            <b-form-group style="margin-top: 30px;">
              <b-button block type="submit" variant="success">
                Sign Up
              </b-button>
            </b-form-group>
          </b-form>
        </b-col>
        <b-col cols="1" />
      </b-row>
    </b-container>
  </div>
</template>

<script>
import firebase from 'firebase'
import { mapActions } from 'vuex'

export default {
  data() {
    return {
      email: '',
      password: '',
      confirmPassword: ''
    }
  },
  methods: {
    signUp() {
      event.preventDefault()
      console.log('sign up.')
    }
  }
}
</script>

スクリーンショット 2019-07-30 10.22.04.png

ユーザ登録処理実装

VuexのアクションからFirebaseユーザ登録→ログイン→トップに遷移の実装を行います。
actionのhelperをインポート。

import { mapActions } from 'vuex'

methods内にaction helperを用いて使用アクションの定義を行います。

...mapActions('user', ['createUser'])

signUpメソッド内にactoinのcreateUserを叩いてユーザ登録、ログインを行い、正常に処理が通ればトップに遷移する処理を実装します。

signUp(event) {
  event.preventDefault()
  if (this.password !== this.confirmPassword) {
    alert('Not match confirm password.')
    return
  }
  const user = {
    email: this.email,
    password: this.password
  }
  this.createUser(user)
    .then(() => {
      this.$router.push('/')
    })
    .catch((error) => {
      alert(error)
    })
}

ここで実際に画面からメールアドレス、パスワードを入力し、submitすると一連の処理が正常に処理され、トップに遷移します。
FirebaseコンソールのAuthenticationのユーザを開くと先ほど入力したユーザが登録されます。

middlewareでログイン認証

middleware 設定

Nuxt.jsのmiddlewareの機能を使ってログイン済みかそうでないかの判定を行います。
Nuxtのmiddlewareはページ(パス)が変わりレンダリングされる前に実行する処理を実装することができる機能です。
middlewareディレクトリにauthenticated.jsを作成。

touch middleware/authenticated.js

nuxt.config.jsにmiddlewareの登録を行います。

export default {
  router: {
    middleware: 'authenticated'
  }
}

ログイン認証の実装

ここからログイン認証済みかそうでないかの処理を実装します。
FirebaseのonAuthStateChangedを使うとログイン済みであればユーザの情報が返ってくるのでこれを使用。
処理としては

  • ログインしていない場合はログインページ、ユーザ登録ページ以外のページには飛ばないようにする
  • ログイン済みの場合でVuexのstoreにログイン情報がない場合はstateに保存する
  • ログイン済みの場合でログインページ、ユーザ登録ページに遷移しないようにする

これらを満たす処理を実装します

import firebase from 'firebase'

const auth = async ({ store, route, redirect }) => {
  const user = await new Promise((resolve, reject) => {
    firebase.auth().onAuthStateChanged((user) => resolve(user))
  })
  if (!user && !(route.name === 'login' || route.name === 'createAccount')) {
    redirect('/login')
  }
  if (user && !store.getters['user/isAuthenticated']) {
    const { displayName, email } = user
    store.dispatch('user/setUserData', { displayName, email })
  }
  if (user && (route.name === 'login' || route.name === 'createAccount')) {
    redirect('/')
  }
}

export default auth

これでログイン済みであればリロードしてもトップページに遷移してくれるようになりました。

まとめ

ログイン処理周りはFirebaseAuthentication使えばかなり簡単に実装ができます。
またNuxtのmiddlewareを使うことでログイン認証も簡単に実装できました。
Nuxtはmiddlewareの他にもルーティング簡単にできたりasyncDataメソッドとか便利な機能が多くていいですね。

次回はFirebase Cloud Messagingを使ってプッシュ通知の実装を行います。

shinbey221
ふろんとえんどえんじにあ。
kaonavi
クラウド人材管理ツール「カオナビ」の製造・販売・サポートを行い、企業の人材管理にイノベーションを起こすことを目的としている会社
https://www.kaonavi.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away