Nuxt3&Firebase Auth v9を試してみたときの備忘録。
Nuxt2&Firebase Auth v8とは結構違うので、びっくりする(*´ω`*)
まずはインストール
$ npm i firebase
プラグインでFirebaseの初期化
Firebaseの初期化はプラグインでおこなう。
.client
サフィックスで、クライアントのみになるので便利
// ~/plugins/firebase.client.ts import { initializeApp } from 'firebase/app' import { defineNuxtPlugin } from '#app' export default defineNuxtPlugin(() => { const config = useRuntimeConfig() const firebaseConfig = { apiKey: config.FB_API_KEY, authDomain: config.FB_AUTH_DOMAIN, projectId: config.FB_PROJECT_ID, storageBucket: config.FB_STORAGE_BUCKET, messagingSenderId: config.FB_MESSAGING_SENDER_ID, appId: config.FB_APP_ID, measurementId: config.FB_MEASUREMENT_ID, } initializeApp(firebaseConfig) })
環境変数を利用するので、.env
も用意する。
# .env # Firebase Config FB_API_KEY=XXXXXXXXXXXXXX FB_AUTH_DOMAIN=xxxx.firebaseapp.com FB_PROJECT_ID=xxxx FB_STORAGE_BUCKET=xxxx.appspot.com FB_MESSAGING_SENDER_ID=123456789012 FB_APP_ID=XXXXXXXXXXXXXX FB_MEASUREMENT_ID=G-XXXXXXXXXX
useRuntimeConfig()
で取得できるように、nuxt.config.ts
にも追加。
クライアント側で取得できるようruntimeConfig.public
配下に設定。
// ~/nuxt.config.ts import { defineNuxtConfig } from 'nuxt' export default defineNuxtConfig({ runtimeConfig: { public: { FB_API_KEY: process.env.FB_API_KEY || "", FB_AUTH_DOMAIN: process.env.FB_AUTH_DOMAIN || "", FB_PROJECT_ID: process.env.FB_PROJECT_ID || "", FB_STORAGE_BUCKET: process.env.FB_STORAGE_BUCKET || "", FB_MESSAGING_SENDER_ID: process.env.FB_MESSAGING_SENDER_ID || "", FB_APP_ID: process.env.FB_APP_ID || "", FB_MEASUREMENT_ID: process.env.FB_MEASUREMENT_ID || "", }, }, })
認証関係のComposable
vueuseの処理を参考にuseAuth.ts
を用意する。Google認証の例。
- @vueuse/firebase | VueUse
// ~/composables/useAuth.ts import type { Auth, User } from "firebase/auth"; import { getAuth, onAuthStateChanged, signInWithPopup, signOut, GoogleAuthProvider, } from "firebase/auth"; import { computed, ref } from "vue"; export function useAuth(auth: Auth = getAuth()) { // ******************************************************** // * data // ******************************************************** const user = ref<User | null>(auth.currentUser); const isAuthed = computed(() => !!user.value); // idTokenが変化したら更新する auth.onIdTokenChanged((authUser) => (user.value = authUser)); // ******************************************************** // * methods // ******************************************************** // 認証状態チェック async function checkAuthState() { try { const _user = await _checkAuthState(auth); user.value = _user; } catch (error) { user.value = null; } } // ログイン async function login() { try { const provider = new GoogleAuthProvider(); await signInWithPopup(auth, provider); } catch (error) { throw error; } } // ログアウト async function logout() { try { await signOut(auth); user.value = null; } catch (error) { throw error; } } return { isAuthed, user, checkAuthState, login, logout }; } // ******************************************************** // * utils // ******************************************************** // onAuthStateChangedのPromise版Util async function _checkAuthState(auth: Auth) { return new Promise<User | null>((resolve, reject) => { // client only if (process.server) return resolve(null); onAuthStateChanged( auth, (user) => resolve(user || null), (error) => reject(error) ); }); }
認証チェック
認証必須のページを設定できるように、middlewareを用意。
isAuthed
もuser
もRef
なので注意(.value
が必要)。
// ~/middleware/auth.ts export default defineNuxtRouteMiddleware(async () => { if (!process.server) { const { isAuthed, checkAuthState } = useAuth(); await checkAuthState(); if (!isAuthed.value) { return await navigateTo("/login", { replace: true }); } } });
pages配下では、<script setup>
内のdefinePageMeta()
で、
利用するmiddlewareを指定。
<script setup> definePageMeta({ middleware: ['auth'] }) </script>
ログインページ用に、ログイン済みならトップページに移動する処理があってもよさそう。
// ~/middleware/auth-login.ts export default defineNuxtRouteMiddleware(async () => { if (!process.server) { const { isAuthed, checkAuthState } = useAuth(); await checkAuthState(); if (isAuthed.value) { return await navigateTo("/", { replace: true }); } } });
ログイン後に元のページに戻る
未認証時に~/middleware/auth.ts
で遷移したあと、
元のページに戻るのは、useRoute().redirectedFrom()
を使うとよいっぽい
// ログイン後に元のページへ戻る const to = useRoute().redirectedFrom?.fullPath || '/' navigateTo(to, { redirectCode: 302 })
おまけ: エミュレータを利用する
エミュレータを利用する場合は、connectAuthEmulator()
などを使う。
環境変数(USE_EMULATOR
)を用意しておいて、切り替えれるようにするのも便利
// ~/plugins/firebase.client.ts import { initializeApp } from "firebase/app"; import { connectAuthEmulator, getAuth } from "firebase/auth"; import { connectFirestoreEmulator, getFirestore } from "firebase/firestore"; import { connectFunctionsEmulator, getFunctions } from "firebase/functions"; export default defineNuxtPlugin(() => { const config = useRuntimeConfig() const firebaseConfig = { // ... } const app = initializeApp(firebaseConfig); // setup emulators if (config.USE_EMULATOR === "true") { connectAuthEmulator(getAuth(app), "http://localhost:9099"); connectFirestoreEmulator(getFirestore(app), "localhost", 8080); const functions = getFunctions(app, "asia-northeast1"); connectFunctionsEmulator(functions, "localhost", 5001); } })
以上!! 変わってるところも多いけど、なれるとだいぶ楽っぽい(*´ω`*)