Nuxt4 と Firebase Authentication を使って 「メールアドレスとパスワードでログインできる仕組み」を最短で実装する手順をまとめました。 Firebase プロジェクト作成済みを前提に、
- Firebase Auth の有効化
- Nuxt 側の設定
- ログイン画面の実装
- ログイン状態の監視
までを コピペで動く形 で紹介します。
Firebase Authentication の設定
まずは Firebase コンソール側で、メール/パスワード 認証を有効化しておきます。
手順はシンプルです。
ここはスクリーンショットを見ながら進めればすぐ終わると思います。
- Firebase コンソールのAuthentication を開く
- ログイン方法を開き、「メール/パスワード」を有効にする
- Firebase の設定値の確認方法
- テスト用ユーザーを作成しておく
1. Authentication を開く

2 .ログイン方法を開き、「メール/パスワード」を有効にする


3. Firebase の設定値の確認方法
ログイン方法を設定したらNuxtとの連携のため、Firebase SDKの設定値を確認しておきましょう。
「設定」→「全般」→「マイアプリ」より確認できます。

4. テストユーザーを登録しておく
ログインテスト用にテスト用のユーザー情報を作成しておきます。
「Authentication」→「ユーザー」→「ユーザーを追加」より、テストユーザーを追加します。

Nuxt 側に Firebase SDK を追加する
Nuxt 側では、まず Firebase の JavaScript SDK を npm で追加します。
Firebase の公式セットアップでも、Web アプリでは npm で firebase パッケージを入れる流れが案内されています。
npm install firebaseFirebase の設定を Nuxt で管理する
Firebase の設定値は .env に書き、Nuxt 側では runtimeConfig.public から使う形にしました。
まず、プロジェクト直下に .env ファイルを新規作成します。
すでにある場合は、その中に Firebase の設定値を追加します。
.env
NUXT_PUBLIC_FIREBASE_API_KEY=xxxxx
NUXT_PUBLIC_FIREBASE_AUTH_DOMAIN=xxxxx.firebaseapp.com
NUXT_PUBLIC_FIREBASE_PROJECT_ID=xxxxx
NUXT_PUBLIC_FIREBASE_STORAGE_BUCKET=xxxxx.firebasestorage.app
NUXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=xxxxx
NUXT_PUBLIC_FIREBASE_APP_ID=xxxxxxxxxx の部分は、Firebase Console に表示される自分の設定値に置き換えてください。
次に、nuxt.config.ts で runtimeConfig.public に渡します。
nuxt.config.ts
export default defineNuxtConfig({
runtimeConfig: {
public: {
firebaseApiKey: process.env.NUXT_PUBLIC_FIREBASE_API_KEY,
firebaseAuthDomain: process.env.NUXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
firebaseProjectId: process.env.NUXT_PUBLIC_FIREBASE_PROJECT_ID,
firebaseStorageBucket: process.env.NUXT_PUBLIC_FIREBASE_STORAGE_BUCKET,
firebaseMessagingSenderId: process.env.NUXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
firebaseAppId: process.env.NUXT_PUBLIC_FIREBASE_APP_ID,
},
},
})nuxt.config.ts は Nuxt 全体の設定を書くファイルです。runtimeConfig はアプリ内で使う設定値の置き場で、runtimeConfig.public の値はブラウザ側からも利用でき、useRuntimeConfig() で参照できます。
Firebase 初期化用ファイルを作る
Firebase の初期化は app/utils/firebase.ts にまとめました。
app/utils/firebase.ts
import { initializeApp, getApps, getApp } from 'firebase/app'
export const getFirebaseApp = () => {
const config = useRuntimeConfig()
const firebaseConfig = {
apiKey: config.public.firebaseApiKey,
authDomain: config.public.firebaseAuthDomain,
projectId: config.public.firebaseProjectId,
storageBucket: config.public.firebaseStorageBucket,
messagingSenderId: config.public.firebaseMessagingSenderId,
appId: config.public.firebaseAppId,
}
// getApps() が空なら initializeApp()、既に初期化済みなら getApp()
// → Nuxt の HMR や複数コンポーネントで初期化が二重になるのを防ぐため
return getApps().length ? getApp() : initializeApp(firebaseConfig)
}getFirebaseApp() は、Firebase の初期化済み app を返すための共通関数です。
今後、Authentication だけでなく Firestore など他の Firebase 機能を使うときも、この関数を入口として使えます。
今回は initializeApp() で Firebase を初期化しつつ、すでに初期化済みの場合は getApps() と getApp() で既存の app を返すようにしています。
こうしておくことで、同じアプリを何度も初期化してしまうのを防げます。
ログイン画面を作る
次に、メール/パスワード でログインできる画面を作ります。
今回作成するログイン画面の動作をご紹介します。
ログイン画面機能紹介

Firebaseコンソールで追加したユーザー情報を入力し、ログインボタンを押すことによって、ログイン成功の表示となります。
ログイン後は、ログイン済みメッセージとユーザー情報を表示するようにしました。
現段階ではユーザー名は設定されてないため、「未設定」と表示されています。
合わせて、ログアウトボタンも付けています。

それ以外のユーザー情報では「ログインに失敗しました」と表示されます。

ログイン画面コード
ログイン画面のコードを全てご紹介します。
ある程度スタイルも整えているためCSSなども含め全て記載しております。
今回のログイン画面では、入力値・エラーメッセージ・送信中状態・ログイン中ユーザーを ref で管理しています。
特に currentUser は、現在ログインしているユーザー情報を入れておくための値です。
ログイン中ならユーザー情報、未ログインなら null が入ります。
<script setup lang="ts">
import {
getAuth,
signInWithEmailAndPassword,
signOut,
onAuthStateChanged,
type User,
} from "firebase/auth";
const email = ref("");
const password = ref("");
const errorMessage = ref("");
const loading = ref(false);
const currentUser = ref<User | null>(null);
const app = getFirebaseApp();
const auth = getAuth(app);
onMounted(() => {
onAuthStateChanged(auth, (user) => {
currentUser.value = user;
});
});
const login = async () => {
errorMessage.value = "";
loading.value = true;
try {
await signInWithEmailAndPassword(auth, email.value, password.value);
} catch (error) {
console.error(error);
errorMessage.value = "ログインに失敗しました";
} finally {
loading.value = false;
}
};
const logout = async () => {
errorMessage.value = "";
try {
await signOut(auth);
} catch (error) {
console.error(error);
errorMessage.value = "ログアウトに失敗しました";
}
};
</script><template>
<div class="page">
<div class="card">
<h1 class="title">Firebase ログイン</h1>
<p class="subtitle">メールアドレスとパスワードでログインします</p>
<div v-if="currentUser" class="user-box">
<p class="success">ログインしました</p>
<p class="user-text">
ユーザー名:
<span>{{ currentUser.displayName || "未設定" }}</span>
</p>
<p class="user-text">
メールアドレス:
<span>{{ currentUser.email }}</span>
</p>
<button class="button logout-button" @click="logout">
ログアウト
</button>
</div>
<form v-else class="form" @submit.prevent="login">
<div class="form-group">
<label for="email">メールアドレス</label>
<input
id="email"
v-model="email"
type="email"
placeholder="example@test.com"
/>
</div>
<div class="form-group">
<label for="password">パスワード</label>
<input
id="password"
v-model="password"
type="password"
placeholder="パスワードを入力"
/>
</div>
<button class="button login-button" type="submit" :disabled="loading">
{{ loading ? "ログイン中..." : "ログイン" }}
</button>
</form>
<p v-if="!currentUser" class="footer-text">
アカウントをお持ちでない方は
<NuxtLink to="/signup" class="footer-link">新規登録はこちら</NuxtLink>
</p>
<p v-if="errorMessage" class="error-message">{{ errorMessage }}</p>
</div>
</div>
</template><style scoped>
.page {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background: #f5f7fb;
padding: 24px;
}
.card {
width: 100%;
max-width: 420px;
background: #ffffff;
border-radius: 16px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08);
padding: 32px;
}
.title {
margin: 0 0 8px;
font-size: 28px;
font-weight: 700;
text-align: center;
color: #222;
}
.subtitle {
margin: 0 0 24px;
text-align: center;
color: #666;
font-size: 14px;
}
.form {
display: flex;
flex-direction: column;
gap: 18px;
}
.form-group {
display: flex;
flex-direction: column;
gap: 8px;
}
.form-group label {
font-size: 14px;
font-weight: 600;
color: #333;
}
.form-group input {
padding: 12px 14px;
border: 1px solid #d8dee9;
border-radius: 10px;
font-size: 14px;
outline: none;
transition: 0.2s;
}
.form-group input:focus {
border-color: #4f46e5;
box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.12);
}
.button {
width: 100%;
border: none;
border-radius: 10px;
padding: 12px 16px;
font-size: 15px;
font-weight: 700;
cursor: pointer;
transition: 0.2s;
}
.button:disabled {
opacity: 0.7;
cursor: not-allowed;
}
.login-button {
background: #4f46e5;
color: white;
}
.login-button:hover {
background: #4338ca;
}
.logout-button {
margin-top: 16px;
background: #ef4444;
color: white;
}
.logout-button:hover {
background: #dc2626;
}
.user-box {
background: #f8fafc;
border: 1px solid #e5e7eb;
border-radius: 12px;
padding: 20px;
}
.success {
margin: 0 0 16px;
color: #16a34a;
font-weight: 700;
font-size: 16px;
}
.user-text {
margin: 8px 0;
color: #333;
}
.user-text span {
font-weight: 600;
}
.error-message {
margin-top: 16px;
color: #dc2626;
font-size: 14px;
text-align: center;
}
</style>onAuthStateChanged() でログイン状態を監視する
onAuthStateChanged() は、Firebase Authentication のログイン状態の変化を監視する関数です。
ページを開いたときや、ログイン・ログアウトしたときに呼ばれ、ログイン中なら user、未ログインなら null が返ります。
今回はその値を currentUser に入れることで、ログインフォームとログイン後の表示を切り替えています。
onMounted(() => {
onAuthStateChanged(auth, (user) => {
currentUser.value = user;
});
});signInWithEmailAndPassword
signInWithEmailAndPassword() は、メール/パスワード 認証でログインするための関数です。
第1引数には Firebase Authentication のインスタンス、
第2引数にはメールアドレス、
第3引数にはパスワードを渡します。
今回は、入力欄で受け取った email.value と password.value を使ってログインしています。
await signInWithEmailAndPassword(auth, email.value, password.value);ログイン後の表示
ログイン後は、ログイン済みメッセージとユーザー情報を表示するようにしました。
合わせて、ログアウトボタンも付けています。
currentUser には、Firebase Authentication が持っているユーザー情報が入ります。
たとえば email や displayName などを参照できます。
ただし、メール/パスワード 認証でユーザーを作成した直後は displayName が未設定のことも多いため、今回はメールアドレスも一緒に表示するようにしました。
次にやること
今回は、Nuxt で Firebase Authentication を使った メール/パスワード ログイン の基本実装までをまとめました。
次回以降は、認証機能をもう少し広げて、以下のようなところを追加していく予定です。
- 新規登録
- パスワードリセット
- プロフィール表示
displayNameの更新
そのあとで、Firestore を使ったデータ保存や一覧取得にも進めていこうと思います。
