Nuxt3とfirebase9で認証機能設定する方法
各バージョン
Nuxt: 3.0.0-rc.11
Firebase: 9.13.0
インストール
npm install firebase
でpackage.json
にfirebase
が追加されればOK
Firebaseの初期設定
■ plugins/firebase.client.ts
import { initializeApp } from 'firebase/app' import { defineNuxtPlugin } from '#app' export default defineNuxtPlugin(() => { const config = useRuntimeConfig() const firebaseConfig = { apiKey: config.FIREBASE_API_KEY, authDomain: config.FIREBASE_AUTH_DOMAIN, projectId: config.FIREBASE_PROJECT_ID, storage_bucket: config.FIREBASE_STORAGE_BUCKET, messaging_sender_id: config.FIREBASE_MESSAGING_SENDER_ID, api_id: config.FIREBASE_API_ID, measurement_id: config.FIREBASE_MEASUREMENT_ID, } initializeApp(firebaseConfig) })
(Authenticationのみで利用するので、初期化時の引数はapiKeyとauthDomainとprojectIdの3つを最低限指定しておけば問題なく動く。)
- plugins ディレクトリについて
- pluginsディレクトリ配下のjsファイルは自動的に読まれるので、nuxt.config.jsへの設定は不要。
- defineNuxtPluginというplugin読み込み用の関数が用意されているので、これを使う。
- プラグイン内でランタイムの設定を使用したい場合は、defineNuxtPlugin 関数内でuseRuntimeConfig() を使用する
Nuxt3はdotenvが内包されているので.envでお手軽に環境変数を設定する
■ .env
FIREBASE_API_KEY=**************** FIREBASE_AUTH_DOMAIN=**************** FIREBASE_PROJECT_ID=**************** FIREBASE_STORAGE_BUCKET=**************** FIREBASE_MESSAGING_SENDER_ID=**************** FIREBASE_API_ID=**************** FIREBASE_MEASUREMENT_ID=G-****************
nuxt3には、実行時に設定値をロードするための仕組みとしてruntimeConfigというものがある。コンポーネント内からはuseRuntimeConfigや$configで値にアクセスすることができる。
■ nuxt.config.ts
runtimeConfig: { public: { FIREBASE_API_KEY: process.env.FIREBASE_API_KEY || '', FIREBASE_AUTH_DOMAIN: process.env.FIREBASE_AUTH_DOMAIN || '', FIREBASE_PROJECT_ID: process.env.FIREBASE_PROJECT_ID || '', FIREBASE_STORAGE_BUCKET: process.env.FIREBASE_STORAGE_BUCKET || '', FIREBASE_MESSAGING_SENDER_ID: process.env.FIREBASE_MESSAGING_SENDER_ID || '', FIREBASE_API_ID: process.env.FIREBASE_API_ID || '', FIREBASE_MEASUREMENT_ID: process.env.FIREBASE_MEASUREMENT_ID || '', }, },
例)config.FIREBASE_API_KEY
と記載すれば.envで設定されたFIREBASE_API_KEYの値が取得できる
各認証メソッド
認証の処理はcomposableにまとめる。
Composables directoryではこちらのドキュメントの通り、各Componentでimportを書かなくても呼び出しができるものになります。
■ composables/useAuth.ts
import { getAuth, createUserWithEmailAndPassword, signInWithEmailAndPassword, signOut as firebaseSignOut, onAuthStateChanged, GoogleAuthProvider, signInWithPopup, TwitterAuthProvider } from 'firebase/auth' export const useAuth = () => { const token = useState<string>('token', () => null) // メールアドレス新規登録関数 async function signUp(email:string, password:string){ return await new Promise((resolve)=>{ // getAuth()でAuthを取得 const auth = getAuth() // メールアドレスとパスワードでアカウントを作成する createUserWithEmailAndPassword(auth, email, password) .then((userCredential) => { console.log(userCredential) // サインアップできたらログインする resolve("success") }) .catch((error) => { console.log(error) const errorMessage = error.message; resolve(errorMessage) }) }) } // メールアドレスとパスワードでログインする関数 async function signIn(email: string, password: string) { return await new Promise<void>((resolve, reject) => { const auth = getAuth() // メールアドレスとパスワードでログインする return signInWithEmailAndPassword(auth, email, password) .then((userCredential) => { userCredential.user .getIdToken() .then((idToken) => { token.value = idToken resolve() }) .catch(reject) }) .catch(reject) }) } // ログアウトする関数 async function signOut() { return await new Promise<void>((resolve, reject) => { const auth = getAuth() // ログアウトする firebaseSignOut(auth) .then(() => { token.value = null resolve() }) .catch((error) => { reject(error) }) }) } // ログイン状態確認関数 async function checkAuthState() { return await new Promise<void>((resolve, reject) => { // client only if (process.server) return resolve() const auth = getAuth() onAuthStateChanged( auth, (user) => { if (user) { user .getIdToken() .then((idtoken) => { token.value = idtoken resolve() }) .catch(reject) } else { token.value = null resolve() } }, (error) => { reject(error) } ) }) } // google認証関数 async function loginWithGoogle() { const auth = getAuth(); const provider = new GoogleAuthProvider(); signInWithPopup(auth, provider) .then((result) => { const credential = GoogleAuthProvider.credentialFromResult(result); const token = credential?.accessToken; const user = result.user; console.log({ credential, token, user }); }) .catch((error) => { const errorCode = error.code; const errorMessage = error.message; const email = error.email; const credential = GoogleAuthProvider.credentialFromError(error); console.log({ errorCode, errorMessage, email, credential }); }); }; // twitter認証関数 async function loginWithTwitter() { const auth = getAuth(); const provider = new TwitterAuthProvider(); signInWithPopup(auth, provider) .then((result) => { const credential = TwitterAuthProvider.credentialFromResult(result); const token = credential?.accessToken; const user = result.user; console.log({ credential, token, user }); }) .catch((error) => { const errorCode = error.code; const errorMessage = error.message; const email = error.email; const credential = TwitterAuthProvider.credentialFromError(error); console.log({ errorCode, errorMessage, email, credential }); }); }; return { signUp, signIn, signOut, token, checkAuthState, loginWithGoogle, loginWithTwitter } }
ログイン状態の永続性
■ middleware/auth.ts
export default defineNuxtRouteMiddleware(async () => { if (!process.server) { const { checkAuthState, token } = useAuth() await checkAuthState() if (!token.value) { return navigateTo('/login', { replace: true }) } } })
Page コンポーネントや Layout コンポーネントにて、使用する middleware を指定すると、コンポーネントが作成 (setup) される前に(ページ遷移に先立ち) middleware 内の指定のファイルが実行されます。
<script setup> definePageMeta({ middleware: ['auth'] }) </script>
参考
Nuxt3 & Firebase9でFirebase Authentication決定版
Nuxt3のruntimeConfigで環境変数を設定する | デバッグライフ