この記事は、Nuxt.js #2 Advent Calendar 2018の12日目の記事です。
概要
この記事ではNuxt.jsでプロジェクトの作成からユーザ認証関係(ログインしないと見れないページの作成方法)を紹介します。
具体的には認証を行うコンポーネントやプラグインを作成し、認証まわりの処理をおこないます。コンポーネントで作成するため再利用性は高い(はず)です。ご利用ください。
※Firebase, Nuxt, Vue初心者が書いた記事です。もっといい書き方とかあれば教えてください!
前提
node
は10.14.1, npm
は6.4.1を使っていきます。
$ node -v
v10.14.1
$ npm -v
6.4.1
1. プロジェクトの作成
Nuxt.js x Firebase事始め - the industrialを見ると、Nuxt.jsのプロジェクトの作成からFirebaseへのデプロイができます。
とりあえず、この記事だけで完結できるように必要なコマンドを説明します。
まず, Nuxtのプロジェクトを作成します。
ここらへんは以下の公式のリファレンスと一緒です。
インストール - Nuxt.js
npx create-nuxt-app <project-name>
途中でいろいろ聞かれますが、基本エンター連打で大丈夫です。(お好きに選んでください)
cd <project-name>
npm run dev
をして、http://localhost:3000でページが見れます。
2. Firebaseのプラグインの作成
Firebaseをプロジェクトに追加します。package.jsonに追加したいので--save
オプションを忘れずに。
npm install firebase --save
次にプロジェクトのpluginsの中にfirebase.jsを作成し、以下のように自分のFirebaseのプロジェクトのキーを記入します。
import firebase from 'firebase'
if (!firebase.apps.length) {
firebase.initializeApp({
apiKey: "API_KEY",
authDomain: "APP_ID.firebaseapp.com",
databaseURL: "https://PROJECT_ID.firebaseio.com",
projectId: "PROJECT_ID",
storageBucket: "PROJECT_ID.appspot.com",
messagingSenderId: "1234567890"
})
}
export default firebase
キーの見方はFirebaseの自分のプロジェクトにいき、歯車アイコンをクリックすると下部に以下の項目があります。このウェブアプリにFirebaseを追加をクリックし、
表示される以下のconfig
を上記のコードのfirebase.initializeApp({...})
に記入してください。
これで自分のプロジェクトと紐付けができました。
これ以降このプロジェクトでFirebase関係のAPIを用いる時はimport firebase from '~/plugins/firebase'
をして使います。するとキーが入ったfirebase
が使えるようになります。
Firebaseのキーをコードにそのまま書いてもいいのかという問題点については以下の記事をご覧ください。結論から言うとDB関係はセキュリティルールで守るためキーをコードに書いても大丈夫です。もちろんdotenvとか使ってもいいかもしれません。
javascript - Is it safe to expose Firebase apiKey to the public? - Stack Overflow
また、プラグインの作成は以下の記事を参考にさせていただきました。
Nuxt + Firebase Authentication | shimar's blog
3. ユーザデータの保存の準備
ログインしたあとにユーザ認証データをユーザの端末に保存するためvuexを使います。
~/store/index.js
に以下のようにかきます。
export const strict = false
export const state = () => ({
user: null,
})
export const mutations = {
setUser (state, payload) {
state.user = payload
}
}
export const actions = {
setUser ({ commit }, payload) {
commit('setUser', payload)
}
}
export const getters = {
isAuthenticated (state) {
return !!state.user
}
}
Firebaseで認証を行うのですが、行なったあとにこのsetUser
を用いて認証したデータを保存しておきます。認証が必要な各ページではこのuser
データを読み込みをログイン済みか検証します。
またVuexはロードするとデータが消えてしまうので、消さないようにユーザのlocalStorageを用いていい感じに保存できるようにしておきます。そのためにvuex-persistedstate
を使います。
以下の記事を参考にnpm install vuex-persistedstate --save
、persistedstate.js
の作成、nuxt.config.js
内の設定変更を行なってください。良記事感謝感激謝謝茄子
nuxt.jsを使う時にlocalStorageでstoreを永続化する - Qiita
これでロードしてもVuexのデータが消えなくなりました!
ただひとつ難点があり、createdやmounted時にVuexの値が取得できません。以下の記事を参考
あーありがち - Vuex PersistedStateの値の復帰のタイミングが謎だったのでNuxtのplugin書いて解決した
そのため、Vuexの値をページロード時に最初に取得し、なにかする場合には以下のように書く必要があります。(他の書き方もあります)
import { mapState } from 'vuex'
export default {
computed: {
...mapState(['user'])
},
mounted() {
console.log(this.user) // ここだと取得できない
setTimeout(() => {
console.log(this.user) // ここだと取得できる
// なにかしらの処理
})
}
}
4. 認証画面、認証コンポーネントの作成
まずログイン画面(/account)のページを作成します。
ログインはメールアドレスとパスワードでログインすることを想定しています。
<template>
<div>
<!-- ログイン中に表示される画面 -->
<div v-if="isAuthenticated">
{{ user.email }}でログイン中です<br>
<button v-on:click="logout">ログアウト</button><br>
<a href="/member-page">メンバーページへ</a>
</div>
<!-- ログインしていない時に表示される画面 -->
<div v-else>
メール<br>
<input type="text" v-model="email"><br>
パスワード<br>
<input type="password" v-model="password"><br>
<button v-on:click="login">ログイン</button>
</div>
</div>
</template>
<script>
import firebase from '~/plugins/firebase'
import { mapActions, mapState, mapGetters } from 'vuex'
export default {
data() {
return {
email: '',
password: ''
}
},
computed: {
...mapState(['user']),
...mapGetters(['isAuthenticated'])
},
mounted() {
firebase.auth().onAuthStateChanged((user) => {
this.setUser(user)
})
},
methods : {
...mapActions(['setUser']),
login() {
firebase.auth().signInWithEmailAndPassword(this.email, this.password)
.then(user => {
// ログインしたら飛ぶページを指定
// this.$router.push("/member-page")
}).catch((error) => {
alert(error)
});
},
logout() {
firebase.auth().signOut()
.then(() => {
this.setUser(null)
}).catch((error) => {
alert(error)
})
}
}
}
</script>
実際に作成したaccountページのhttp://localhost:3000/accountに飛ぶと以下のような感じ
おっと、まだアカウントを作成していませんでしたね!(海外のライブラリのチュートリアルありがちな唐突な気さくさ)
Firebaseの管理画面でAuthenticationの項目のログイン方法のメール/パスワード
を有効にしておく必要があります。ここで、Firebaseの管理画面のAuthenticationの項目のユーザから適当にユーザを追加しておいてください。
これでログインできるようになりました。
実際に作成したアカウントでログインをしてみると以下のような感じです。
実際にサービスにする際には新規アカウント作成画面を作ったりしてあげる必要があります。(余裕があれば書きたい)
また、GoogleやTwitter,Facebookアカウントを利用したログインとかもFirebaseで**めっちゃ簡単に実装できます。**スマホユーザのことを考えると該当のログイン画面にリダイレクトしてあげて本サービスにまたリダイレクトしてあげる必要があります。このリダイレクトがめんどくて、サービスのルートにリダイレクトされるので、そこらへんをいい感じに書いてあげる必要があります。(余裕があれば書きたい)
5. 認証が必要な画面を簡単に作成
まず認証をしていないと見れないコンポーネント(認証していないとログイン画面に飛ばされるコンポーネント)を作成します。
components
にmembers-only.vue
を作成して以下のコードを書きます。
<template>
<div v-if="loaded">
<slot/>
</div>
</template>
<script>
import { mapGetters, mapState } from 'vuex'
export default {
data() {
return {
loaded: false
}
},
computed: {
...mapGetters(['isAuthenticated'])
},
async mounted() {
setTimeout(() => {
if (!this.isAuthenticated) {
// ログインしていなかったら飛ぶページを設定
this.$router.push('account')
}
this.loaded = true
}, 0)
}
}
</script>
以上のコンポーネントを用いて、実際にログインしていないと見れないページを以下のように作成します。
<template>
<members-only>
<h1>ログインしていないと見れないページ</h1>
<a href="/account">アカウントページへ</a>
</members-only>
</template>
<script>
import MembersOnly from '~/components/members-only.vue'
export default {
components: {
MembersOnly
}
}
</script>
実際に作成したmember-pageページのhttp://localhost:3000/member-pageに飛ぶと以下のような感じ
ログインしているとちゃんと表示されます。ログインしていないとaccountページに飛ばされます。
members-only
コンポーネントを作成したことによって、ログインしていないと見れないページを簡単に作成できました。コンポーネントをロードして、該当のページの親要素にしてあげるだけです。
ちなみにユーザ側でVuexの値はいじることができます。そのため実際はログインしていなくても、ログインしているように見せかけてmembers-only
コンポーネントをつけた内部をみることができます。
FirebaseではDBにセキュリティルールをつけて、ユーザごとの読み書き制限ができます。そのため、以上のような場合でもきちんとデータ側にセキュリティルールをつけておけばいいということになります。つまり悪意のあるユーザが自分のブラウザ上でVuexの値をいじっても、セキュリティルールで守られたデータは取得できないので、大丈夫ということになります。
おわり。
番外編 1ヶ月前の自分に教えたいリンク集
FirebaseのDBやってると「は?リレーション組めないやんけ💩」ってなると思います。それはSQLマンです。Firebaseでは非正規化が正規化です。ってのを教えてくれるFirebaseの公式のYouTubeの動画がコレです。↓
言語は英語ですが、ただしい日本語の字幕がついてるので問題ないです。
SQL Databases and the Firebase Database - The Firebase Database For SQL Developers #1 - YouTube
NoSQLはわかった、でもこれってどうやって読み書き制限すればええんか?ってなった時に使えるのが↓
先ほども触れましたが、FirebaseはAPIキーはアプリケーションを特定するためだけに使い読み書き制限のために用いません。
FirebaseではDBの読み書き制限はセキュリティルールを記述することによって行います。
Firebase Realtime Database ルールについて | Firebase Realtime Database | Firebase
firebase realtime database でよく使う rule - Qiita
FirebaseのDBのクエリの公式リファレンスのサンプル少なくない?ってなった時に使えるのが↓です。
公式リファレンスはわかりやすくするため(?)、必要最低限のことが書いてあります。そのためこれをこうしたいっていうのはググるといいスニペットが出てきます。
SQLをFirebaseで書き換えるためのチート集 - Qiita