Firebase Authenticationを使えば、メールアドレスとパスワードによる認証を短時間で実装できます。この記事では、Vue 3(Composition API)+ Firebase v9(modular SDK)での導入手順から、サインアップ/ログイン/ログアウト、状態監視、ルートガード、パスワードリセット、メール検証(Email Verification)までをまとめて解説します。
準備:FirebaseプロジェクトとAuthの有効化
- Firebase Consoleでプロジェクト作成 → 「ウェブアプリを追加」から構成情報(apiKey 等)を取得
- Authentication > サインイン方法 で「メール/パスワード」を有効化(必要に応じて「メールリンク」も)
依存関係のインストール
npm i firebase
Firebase初期化(authインスタンスのエクスポート)
// src/lib/firebase.ts
import { initializeApp } from 'firebase/app'
import { getAuth, setPersistence, browserLocalPersistence } from 'firebase/auth'
const firebaseConfig = {
apiKey: 'YOUR_API_KEY',
authDomain: 'YOUR_PROJECT.firebaseapp.com',
projectId: 'YOUR_PROJECT_ID',
appId: 'YOUR_APP_ID'
}
const app = initializeApp(firebaseConfig)
export const auth = getAuth(app)
// 永続化(タブを跨いでログイン状態維持)
setPersistence(auth, browserLocalPersistence)
サインアップ/ログイン/ログアウトのComposable
// src/composables/useAuth.ts
import { ref } from 'vue'
import {
createUserWithEmailAndPassword,
signInWithEmailAndPassword,
sendEmailVerification,
sendPasswordResetEmail,
signOut,
onAuthStateChanged,
User
} from 'firebase/auth'
import { auth } from '@/lib/firebase'
const currentUser = ref<User | null>(null)
const loading = ref(true)
const errorMsg = ref('')
// 認証状態を監視(アプリ共通で1回セット)
onAuthStateChanged(auth, (user) => {
currentUser.value = user
loading.value = false
})
function friendlyError(e: unknown) {
const code = (e as any)?.code || ''
if (code.includes('auth/email-already-in-use')) return 'このメールは既に登録済みです'
if (code.includes('auth/invalid-email')) return 'メールアドレスの形式が正しくありません'
if (code.includes('auth/weak-password')) return 'パスワードは6文字以上にしてください'
if (code.includes('auth/user-not-found') || code.includes('auth/wrong-password')) return 'メールまたはパスワードが違います'
return 'エラーが発生しました'
}
export function useAuth() {
const signUp = async (email: string, password: string) => {
errorMsg.value = ''
try {
const cred = await createUserWithEmailAndPassword(auth, email, password)
// メール検証リンクを送信(任意)
await sendEmailVerification(cred.user)
return cred.user
} catch (e) {
errorMsg.value = friendlyError(e)
throw e
}
}
const signIn = async (email: string, password: string) => {
errorMsg.value = ''
try {
const cred = await signInWithEmailAndPassword(auth, email, password)
return cred.user
} catch (e) {
errorMsg.value = friendlyError(e)
throw e
}
}
const resetPassword = async (email: string) => {
errorMsg.value = ''
try {
await sendPasswordResetEmail(auth, email)
} catch (e) {
errorMsg.value = friendlyError(e)
throw e
}
}
const logout = async () => {
await signOut(auth)
}
return { currentUser, loading, errorMsg, signUp, signIn, resetPassword, logout }
}
サインアップ/ログインフォームの例
<template>
<h2>ログイン</h2>
<form @submit.prevent="onSignIn">
<input v-model="email" type="email" placeholder="メールアドレス" required />
<input v-model="password" type="password" placeholder="パスワード" required minlength="6" />
<button :disabled="submitting">ログイン</button>
</form>
<p v-if="errorMsg" style="color:#c00">{{ errorMsg }}</p>
<hr />
<h2>新規登録</h2>
<form @submit.prevent="onSignUp">
<input v-model="email" type="email" placeholder="メールアドレス" required />
<input v-model="password" type="password" placeholder="パスワード" required minlength="6" />
<button :disabled="submitting">登録</button>
</form>
<button @click="onReset" :disabled="!email || submitting">パスワードをお忘れですか?</button>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { useAuth } from '@/composables/useAuth'
const email = ref('')
const password = ref('')
const submitting = ref(false)
const { signIn, signUp, resetPassword, errorMsg } = useAuth()
async function onSignIn() {
submitting.value = true
try { await signIn(email.value, password.value) }
finally { submitting.value = false }
}
async function onSignUp() {
submitting.value = true
try {
await signUp(email.value, password.value)
alert('確認メールを送信しました。メール内のリンクで検証してください。')
} finally { submitting.value = false }
}
async function onReset() {
submitting.value = true
try {
await resetPassword(email.value)
alert('パスワードリセット用のメールを送信しました。')
} finally { submitting.value = false }
}
</script>
メール検証(Email Verification)の運用例
ビジネス要件によっては「メール検証済みユーザーのみ利用可」にする必要があります。以下は、未検証ユーザーのアクセスを制限する例です。
// src/router/guards/emailVerified.ts
import type { Router } from 'vue-router'
import { useAuth } from '@/composables/useAuth'
export function registerEmailVerifiedGuard(router: Router) {
const { currentUser, loading } = useAuth()
router.beforeEach((to, _from, next) => {
if (loading.value) return next() // 初期同期中は通す
const needVerified = to.meta.requiresVerified === true
const user = currentUser.value
if (!needVerified) return next()
if (user && user.emailVerified) return next()
// 未検証
next({ name: 'VerifyNotice' })
})
}
ルートガード(未ログインをログインページへ)
// src/router/index.ts
import { createRouter, createWebHistory } from 'vue-router'
import { useAuth } from '@/composables/useAuth'
const routes = [
{ path: '/login', name: 'Login', component: () => import('@/pages/Login.vue') },
{ path: '/', name: 'Home', component: () => import('@/pages/Home.vue'), meta: { requiresAuth: true } }
]
const router = createRouter({ history: createWebHistory(), routes })
router.beforeEach((to, _from, next) => {
const { currentUser, loading } = useAuth()
if (loading.value) return next() // 初回同期中は保留せず遷移(ページ側でローディング表示推奨)
if (to.meta.requiresAuth && !currentUser.value) {
return next({ name: 'Login', query: { redirect: to.fullPath } })
}
next()
})
export default router
ログアウトボタンの例
<template>
<button @click="onLogout">ログアウト</button>
</template>
<script setup lang="ts">
import { useAuth } from '@/composables/useAuth'
const { logout } = useAuth()
async function onLogout() { await logout() }
</script>
セキュリティ・実運用のポイント
- メール検証の強制:重要機能は
user.emailVerified
を確認してガード - バックエンドの保護:Cloud Functions / API では Firebase Admin SDK でIDトークンを検証
- 永続化戦略:共有PCでは
browserSessionPersistence
を選択するなど要件に応じて設定 - エラーメッセージ:ユーザー向けにわかりやすいメッセージへ変換(上記
friendlyError
参照) - レート制限:パスワードリセットや検証メールの連打対策をUI側で
まとめ
Vue 3 と Firebase Authentication を組み合わせると、メールアドレス認証を短時間で実装できます。ComposableでAPI呼び出しを共通化し、ルートガードでアクセス制御、パスワードリセットとメール検証を組み合わせれば、実運用に耐える認証基盤が完成します。必要に応じてGoogle/AppleなどのOAuthプロバイダも同一の構成に追加できます。