在上一篇,我們完成了登入表單的驗證,算是裝好了「基本門鎖」。但如果你常用現代網站或 App,應該會發現:許多平台除了傳統帳號密碼之外,還提供了「社群登入」選項。
今天,我們就來看看怎麼透過 Firebase,快速整合 Google 登入,體驗一下「不用帶鑰匙,用手機一鍵開門」的便利。
本篇重點整理:
Firebase 是 Google 推出的 BaaS (Backend as a Service) 平台,意思就是「把後端交給它代管」。
對前端開發者來說,這就像請了個專業保全公司:你不用自己從頭蓋一棟伺服器大樓,也不用研究一堆複雜的安全規則,Firebase 幫你處理好一切✅。
它的服務很多,舉幾個常見的:
在這篇文章裡,我們會聚焦在 Authentication ( 身份驗證 )。因為對我們這次主題 — 「房門與門鎖」來說,這就是幫使用者配置「電子門禁卡」的核心。
如果系統沒有身份驗證,任何人都能隨便進來亂搞,資料不保,使用體驗也無法個人化。
Firebase Authentication 幫我們解決了這些問題:它提供安全的登入流程、整合各種社群帳號,甚至會自動幫我們管理 Session,不需要自己寫一大堆 Token 保存的程式。
既然要談社群登入,就不能不提 OAuth。
它的全名是 Open Authorization,顧名思義,就是一套「授權」規範。
想像一下🤔,你要用某個任務管理 App,選擇「用 Google 帳號登入」。
整個過程不會是把 Google 的帳號密碼交給那個 App,而是:
目前最常見的版本是 OAuth 2.0,它比舊版更安全、彈性更大。像是:用 Token 代替密碼驗證、支援各種應用情境,還能細分授權範圍(只讀 Email、不碰 Google Drive 等)。
那 Firebase Authentication 呢? 其實它就是幫我們把 OAuth 2.0 這些繁瑣的流程打包好。
本來需要自己寫一堆跳轉、授權、Token 管理的程式碼,現在只要呼叫幾個 API:
換句話說,Firebase 把「複雜的 OAuth 流程」簡化成幾行程式碼,讓前端工程師能快速導入安全的社群登入功能☺️。
講了這麼多,來點實際操作吧!在實作之前,我先提醒一下:
🔰 Firebase 本身是 Google 出品的服務,所以 Google 登入的串接流程算是最簡單的,不需要額外跑去開發者後台申請 API Key,就能直接完成。
如果想要整合其他的登入方式,例如 Meta (Facebook)、X (Twitter)、GitHub,流程就會複雜一點。這些第三方平台都會要求你先到它們的官方開發者平台申請一組 API Key / Secret,再把這些金鑰設定到 Firebase 控制台,最後才能完成串接。
既然如此,我們就先從最簡單的 Google 登入 開始,一步步完成設定!
進入 Firebase Console ,登入帳戶並建立一個新專案。
接著在專案裡新增一個網頁應用程式:
建立完成後,Firebase 會提供初始化流程與一組專案金鑰。
將 firebase 套件下載至專案中
npm i firebase
接著在專案內建立一個 firebase.ts
檔案:
import { initializeApp } from "firebase/app";
import { getAuth, GoogleAuthProvider } from "firebase/auth";
const firebaseConfig = {
apiKey: import.meta.env.VITE_FIREBASE_API_KEY,
authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN,
projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID,
storageBucket: import.meta.env.VITE_FIREBASE_STORAGE_BUCKET,
messagingSenderId: import.meta.env.VITE_FIREBASE_MESSAGING_SENDER_ID,
appId: import.meta.env.VITE_FIREBASE_APP_ID,
measurementId: import.meta.env.VITE_FIREBASE_MEASUREMENT_ID,
};
const app = initializeApp(firebaseConfig);
// Authentication 相關
export const auth = getAuth(app);
export const googleProvider = new GoogleAuthProvider();
🔑 提醒:就像上一篇提到的,千萬不要把金鑰直接寫死在程式碼裡,請放在
.env
中,並透過import.meta.env
讀取。
最後,回到 Firebase Console 左側選單,進入 Authentication,選擇「新增供應商」並啟用 Google。
完成這一步之後,我們就能正式進入程式碼撰寫了🚀
Social-Login.tsx
import type { UserCredential } from "firebase/auth";
import type { IconDefinition } from "@fortawesome/fontawesome-svg-core";
import useSocialLogin from "@utils/socialLoginHandler";
import { LoadingSpinner } from "@components/ui/LoadingSpinner";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { cn } from "@utils/cn";
export interface SocialProvider {
id: string;
name: string;
buttonBgClass: string;
icon: IconDefinition;
authenticate: () => Promise<UserCredential>;
}
interface SocialLoginButtonProps {
onLoginSuccess?: (displayName: string | null, email: string | null) => void;
onLoginError?: (errorMessage: string) => void;
className?: string;
provider: SocialProvider;
}
export default function SocialLogin({
onLoginSuccess,
onLoginError,
className = "",
provider,
}: SocialLoginButtonProps) {
const { handleLogin, isLoading } = useSocialLogin({
onLoginSuccess: (result) =>
onLoginSuccess?.(result.displayName, result.email),
onLoginError,
});
const handleClick = () => {
handleLogin(provider.authenticate());
};
return (
<button
type="button"
onClick={handleClick}
disabled={isLoading}
className={cn(
"p-2 rounded-full text-white text-lg",
"focus:outline-none focus:ring-2 focus:ring-opacity-50",
"shadow-md transition duration-200",
"flex items-center justify-center gap-4",
provider.buttonBgClass,
className,
isLoading && "opacity-70 cursor-not-allowed"
)}
>
{isLoading ? (
<>
<LoadingSpinner />
<span>登入中...</span>
</>
) : (
<>
<FontAwesomeIcon icon={provider.icon} />
<span>{provider.name} 登入</span>
</>
)}
</button>
);
}
socialProvider.ts
import { GoogleAuthProvider, signInWithPopup } from "firebase/auth";
import type { SocialProvider } from ".";
import { auth } from "@lib/firebase";
import { faGoogle } from "@fortawesome/free-brands-svg-icons";
// Google
export const googleProvider: SocialProvider = {
id: "google",
name: "Google",
buttonBgClass: "bg-blue-500 hover:bg-blue-600 focus:ring-blue-500",
icon: faGoogle,
authenticate: () => signInWithPopup(auth, new GoogleAuthProvider()),
};