在做前端登入系統時,你可能會想:「我不是已經用 Firebase Login 登入了嗎?為什麼還要搞一個後端驗證?」別急,這篇文章用最輕鬆的方式帶你理解,還會附上兩種實作方案:真後端 Express + Firebase Admin,以及MSW 模擬後端,適合想快速開發或測試的你。
Firebase SDK 確實可以直接在前端登入,拿到 idToken
,你甚至可以直接呼叫 Firebase 的服務。但如果你的應用有 自訂後端 API,就必須驗證這個 Token。原因有三個:
安裝
npm install express firebase-admin dotenv cors
初始化
先建立 firebase-admin
的設定檔(可用 Service Account JSON):
import admin from "firebase-admin";
import dotenv from "dotenv";
dotenv.config();
admin.initializeApp({
credential: admin.credential.cert(JSON.parse(process.env.FIREBASE_SERVICE_ACCOUNT)),
});
export default admin;
建立 server
import express from "express";
import cors from "cors";
import admin from "./firebaseAdmin.js";
const app = express();
app.use(cors());
app.use(express.json());
// 驗證 Token
app.post("/auth/verify", async (req, res) => {
const { idToken } = req.body;
try {
const decodedToken = await admin.auth().verifyIdToken(idToken);
res.json({ uid: decodedToken.uid, email: decodedToken.email });
} catch (error) {
res.status(401).json({ error: "Token 無效或過期" });
}
});
// 模擬登出
app.post("/auth/logout", (req, res) => {
// Firebase 後端不直接處理前端 Token,登出由前端清除 Token
res.json({ message: "登出成功(前端自行清理 token)" });
});
app.listen(process.env.PORT, () => {
console.log(`Server running on http://localhost:${process.env.PORT}`);
});
這樣你就有一個最小可用的 Firebase 驗證後端了。
如果你不想啟 Express 後端,也可以用 MSW 模擬 API。
安裝 MSW
npm i -D msw
建立 handler
import { rest } from "msw";
export const handlers = [
rest.post("/auth/verify", (req, res, ctx) => {
const { idToken } = req.body;
if (idToken === "fake-token") {
return res(
ctx.status(200),
ctx.json({ uid: "123", email: "user@example.com" })
);
}
return res(ctx.status(401), ctx.json({ error: "Token 無效或過期" }));
}),
rest.post("/auth/logout", (req, res, ctx) => {
return res(ctx.status(200), ctx.json({ message: "登出成功" }));
}),
];
接 React 使用流程
import { setupWorker } from "msw";
import { handlers } from "./handlers";
export const worker = setupWorker(...handlers);
// index.js 或 main.tsx
if (process.env.NODE_ENV === "development") {
const { worker } = await import("./mocks/browser");
worker.start();
}