1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

FirebaseAdvent Calendar 2021

Day 24

Electron + nuxt.js + Firebase v9 でFirebase Authenticationのログイン状態をVuexに保存したい。

Last updated at Posted at 2021-12-23

はじめに

こんにちは。久しぶりのQiitaですが、UIとか変わってておーって感じです。せっかくのクリスマスなので何か書いてみようと書いたのが、「Electron + nuxt.js + Firebase v9 でFirebase Authenticationのログイン状態をVuexに保存したい。」ということで、要素盛りすぎだろ!な感じでいきたいと思います。というのも、ここ半年は文化祭の準備で色々忙しく、ゆっくり考えて開発する時間もなかったわけで、「ここはいっちょでっかいProjectやってみるか!」ということで「TecTask」なるタスク管理ソフトの開発を画策しています。公開してサービスとして展開することは全く考えておらず、自分用として企業並みの機能を兼ね備えたものを作ろうとしている次第です。文化祭についてやったことはおいおい書いていくとしてGithubやGitを使ったり、Nuxt.jsでの開発の経験をぶつけようというわけです。

今回はNode.jsでアプリが開発できちゃうElectronNuxt.jsと併用した環境でFirebase Authenticationを使いたいなという感じです。ElectronFirebaseに関係してくるところは少ないので複数の記事をごっちゃにした感じになってしまいました。以下はこの記事でやっていくことです。

  • Electron + nuxt.js環境を作りたいよ
  • Firebase SDK v9をnuxtで使用したいよ
  • Firebase Authenticationをnuxtで使用したいよ
  • ログインの状態をvuexで保存したいよ

第1章 導入

ここではElectronとNuxtの導入、そしてFirebaseSDKv9の導入をおこなっていきます。

プロジェクトの生成

コマンドライン
npm install -g vue-cli
vue init michalzaq12/electron-nuxt <project-name>

cd <project-name>
yarn install
yarn dev 

ここでは、michalzaq12さんのelectron-nuxtをつかって手っ取り早く環境構築をしていきます。(Github
nuxtのルートフォルダはsrc内のrendererになります。

必要なプラグインの導入

今回はfirebaseとAPIキーを保存するためにdotenvを導入します。

コマンドライン
yarn add firebase
yarn add @nuxtjs/dotenv

僕の環境でのバージョンは以下のとおりです。

package.json(抜粋)
"@nuxtjs/dotenv": "^1.4.1",
"firebase": "^9.6.1"

ここで注意してもらいたいのがfirebaseSDKのバージョンがv9だと言うことです。v8からv9で大きな変更が加えられています。一応、互換性はありますが、v9の解説も兼ねているのでv9で書いていきます。

コマンドライン
firebase init

最後にこのコマンドを実行することでfirestoreなどの設定ファイルをローカルに持ってきます。これが果たして必要かどうかは未検証です。このコマンドの実行前にFirebase Consoleでプロジェクトを生成しとくことをお勧めします。

なお今後、Electronの環境というより、nuxt.jsの環境となるので、これでこの記事でElectronが出てくることは無くなります。ただ、Electron特有の要素があるかもしれないので詳しい方はぜひコメントください。

firebaseの初期化

firebaseはAPIキーなどを用いた初期化が最初に必要になります。それをpluginでおこなっていきます。なお、あらかじめ使用するFirebaseプロジェクトを生成し、設定 > マイアプリからアプリ(今回ならWEBアプリ)を生成し、生成後の画面のSDK の設定と構成からapiKey, authDomain, databaseURL, projectId, storageBucket, messagingSenderId, appId, measurementIdを取得してください。(いつでも設定からみれるのでメモする必要はない。)

直下に.envという名前のファイルを作りAPIキー周りを保存します。

.env
API_KEY=
AUTH_DOMAIN=
DATABASE_URL=
PROJECT_ID=
STORAGE_BUCKET=
MESSAGING_SENDER_ID=
APP_ID=
MEASUREMENT_ID=

また、nuxt.config.jsdotenvを使うことを明記しておきます。

nuxt.config.js(抜粋)
modules: ["@nuxtjs/dotenv"],

つぎに、pluginsフォルダにfirebase.jsを生成して以下を書き込みます。

plugins/firebase.js
import { initializeApp } from "firebase/app";
import { initializeAuth } from "firebase/auth";

const firebaseConfig = {
  apiKey: process.env.API_KEY,
  authDomain: process.env.AUTH_DOMAIN,
  databaseURL: process.env.DATABASE_URL,
  projectId: process.env.PROJECT_ID,
  storageBucket: process.env.STORAGE_BUCKET,
  messagingSenderId: process.env.MESSAGING_SENDER_ID,
  appId: process.env.APP_ID,
  measurementId: process.env.MEASUREMENT_ID,
};

const app = initializeApp(firebaseConfig);

// Initialize Auth
export const auth = initializeAuth(app);

これでFirebaseはInitialize出来ました。なお、最新のnuxtではdotenvの代わりにnuxt標準のRuntimeConfig機能で同じことができます。が、electronのせいなのかなんなのか僕の環境では.envを認識してくれなかったのでdotenvをやむなく使いました。なにかわかる方いらっしゃいますでしょうか。

RuntimeConfigについてはこの記事がわかりやすいです。

最後にpluginを使うことを明記してここは完成です。

nuxt.config.js(抜粋)
module.exports = {
  // 中略...
  plugins: ["@/plugins/firebase.js"]
}

第2章 実装

ログイン処理を書く

Vuexにログイン処理を書いていきます。

storeフォルダ内にauth.jsを生成して以下を書き込みます。

store/auth.js
import { auth } from "@/plugins/firebase.js";
import { onAuthStateChanged, signInWithEmailAndPassword, signOut } from "firebase/auth";

export const state = () => ({
  isLoggedIn: false, // ログイン状態
  user: {},          // ログイン情報
});

export const mutations = {};

// stateの内容を直接とるのではなくgetterを通して取得する方が良い。
export const getters = {
  isAuthenticated: (state) => !!state.isLoggedIn, // ログイン状態
  currentUserInfo: (state) => state.user,         // ログイン情報
};

export const actions = {
  firebaseAuthLogin({ state }, loginInfo) {
    if (!loginInfo.loginMailAddress || !loginInfo.loginPassword) return null;
    signInWithEmailAndPassword(
      auth,
      loginInfo.loginMailAddress,
      loginInfo.loginPassword
    )
      .then((userCredential) => {
        const user = userCredential.user;
        // ログイン成功
      })
      .catch((error) => {
        const errorCode = error.code;
        const errorMessage = error.message;
        // ログイン失敗
      });
  },
  firebaseAuthLogout({ state }) {
    signOut(auth)
      .then(() => {
        state.isLoggedIn = false;
        // ログアウト成功
      })
      .catch((error) => {
        // ログアウト失敗
      });
  },
  setAuthChangedListener({ commit }) {
    onAuthStateChanged(auth, (user) => {
      user = user ? user : {};
      state.user = user;
      state.isLoggedIn = user.uid ? true : false;
    });
  },
};


今回はメール/パスワードでログインすることを想定しました。Firebase AuthenticationSign-in methodで色々なプロバイダ(例えばGoogleやFacebookなど)を利用することができます。その際、firebaseAuthLoginを改変する必要があるのでご注意ください(詳しくはこちら)。

onAuthStateChangedでログイン情報の変化に対応しています。ログインしているか否かはisLoggedInに格納され、 isAuthenticatedを介して取得可能です。firebaseAuthLoginでログイン、firebaseAuthLogoutでログアウトできます。度々出てくる{ state }は自動で与えられるもので、vuex内のstateを参照できます。

また、Vuex内の関数は第二引数までしか渡すことができません。(第一引数は{ state })そこで、引数をオブジェクト型にし、階層をもって三つ以上の引数を渡しています(詳しくはこちら)。

ではどのように使用するのか見てみましょう。

middlewareをつくる

middlewareはページ遷移時に比較的早く呼ばれる部分でページに進入できるかどうかを振り分けます。

middlewareディレクトリ内にcheckAuth.jsを生成し、以下のとおり記述します。

middleware/checkAuth.js
const allowUnauthorizedPages = ["index", "login"];
const banAuthorizedPages = ["login"];

export default function ({ store, route, redirect }) {
  store.dispatch("auth/setAuthChangedListener");
  if (
    !store.getters["auth/isAuthenticated"] &&
    !allowUnauthorizedPages.includes(route.name)
  ) {
    // 未ログインかつ、未ログインで入ってはいけないページの場合はログインページへ
    redirect("/login");
  }
  if (
    store.getters["auth/isAuthenticated"] &&
    banAuthorizedPages.includes(route.name)
  ) {
    // ログイン済みかつ、ログイン済みで入ってはいけないページの場合はホームへ
    redirect("/");
  }
}

allowUnauthorizedPagesは未ログインで入れるページを記録します。このとき、route.nameと比較するのでホームはindexとなることに注意してください。また、banAuthorizedPagesではログイン済みでは入れないページ(例えばログインページなど)を記述してください。それぞれ条件が揃った時にredirectされる仕組みです。

store.dispatch()store/auth.jssetAuthChangedListenerを呼び、ログイン情報に変化があったときのコールバックを登録しときます。なお、auth/setAuthChangedListenerのようにファイル名/Action名を引数に取ります。store.gettersも同様にとります。

最後にこのmiddlewareを全ページに登録して終わりです。

nuxt.config.js(抜粋)
router: {
  middleware: ["checkAuth"],
},

長期的なテストをおこなっていないため、isAuthenticatedはfalseだが、firebase上では認証されていると言う可能性もありますが、ユーザ視点ではセッションが切れた程度にしか感じないためその場合でもログインページに飛ばしています。そもそもそのような状況があるかどうかもわからないのでもし深刻であればfirebase/authに現在のユーザ情報をとる関数があるはずなのでそれを使うといいと思います。

第3章 使い方

ここまでpage本体には手を出してこなかったため、かなりきれいな記述ができ他と思います。ですが、どうしてもpageで書かないといけないことがあります。それはログイン状態やユーザ情報の取得とログインです。

vuexからログイン状態やログイン情報を取得する

pages/index.vue
<template>
  <!-- 中略 -->
  <div>{{ isAuthenticated }}:{{ currentUserInfo }}</div>
  <!-- 中略 -->
</template>

<script>
// 中略...
import { mapGetters } from "vuex";

export default {
  // 中略...
  computed: {
    ...mapGetters("auth", ["isAuthenticated", "currentUserInfo"]),
  },
  // 中略...
}
</script>

mapGetters<template>内にあるように普通の変数として扱えるようになります。

vuexのactionsを叩いてログインする

pages/index.vue
<template>
  <!-- 中略 -->
  <div @click="login">ログイン</div>
  <!-- 中略 -->
</template>

<script>
// 中略...
import { mapActions } from "vuex";

export default {
  // 中略...
  data() {
    return {
      loginMailAddress: "",
      loginPassword: "",
    };
  },
  methods: {
    ...mapActions("auth", ["firebaseAuthLogin"]),
    login() {
      this.firebaseAuthLogin({
        loginMailAddress: this.loginMailAddress,
        loginPassword: this.loginPassword,
      });
    },
  },
  // 中略...
}
</script>

mapActionsで指定したactionsをこちらのmethodsとして扱えるようになります。先述したとおり、メールアドレスとパスワードは第三引数を取れないため、Object型で渡していることがわかると思います。v-modelなどでthis.loginMailAddressthis.loginMailAddressをどこかの<input>と紐づければログイン処理は完成です。なお、null除外処理はしっかり書いた方がいいと思いますが今回は省略しました。

さいごに

今回はかなり急ぎ&雑に書いてしまったのでこのコードが完璧に動くかどうかは未検証です。もしかしたら、書いていない部分で弄り回しててそこが効いてる可能性もありますが...。その場合はご指摘ください。また、electronがいるとRuntimeConfig使えない(?)問題については鋭意調査中ですが何か知っている方がいればコメントお願いします。

今回で一番戸惑ったのはfirebase v9の圧倒的情報不足です...。公式リファレンスを読みまくって実装したのでもしかしたらもっといい実装方法があったかもしれません。それはおいおいみなさんが記事を出してくれることでしょう。そう祈って Merry Christmas –––良いお年を。

p.s.今回参考にした記事様・公式リファレンスです... [【公式リファレンス】ウェブサイトで Firebase Authentication を使ってみる](https://firebase.google.com/docs/auth/web/start?hl=ja) [【公式リファレンス】JavaScript でメールリンクを使用して Firebase 認証を行う](https://firebase.google.com/docs/auth/web/email-link-auth?hl=ja) [【公式リファレンス】認証状態の永続性](https://firebase.google.com/docs/auth/web/auth-state-persistence?hl=ja) [【公式リファレンス】auth package](https://firebase.google.com/docs/reference/js/auth) [【Qiita】【4】Firebase AuthenticationとNuxt.jsのVuexで認証機能を実現する(2)](https://qiita.com/kobori_akira/items/3e64d543023266051a8f) [【Qiita】【nuxt】middlewareについて](https://qiita.com/yama-t/items/97871a4c2f1cd8325078) [【Hatena Blog】Nuxtで画面に横断的にMiddlewareを適用する](https://su-kun1899.hatenablog.com/entry/2018/06/19/200000) [【Hatena Blog】Nuxtで.envファイルを使って環境変数を設定する](https://firebase.google.com/docs/auth/web/start?hl=ja) [【MSeeeeN】Vuex で Action, Mutation に第3引数を渡したくなったら](https://mseeeen.msen.jp/deal-with-multiple-arguments-with-action-or-mutation-in-vuex/) [【Metrocode.co】Firebase Authentication と NUXT(Vue.Js)で簡単にユーザー登録&認証を作成する](https://www.metrocode.co/blog/post/firebase-authentication-nuxt-vuejs)
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?