iT邦幫忙

2025 iThome 鐵人賽

DAY 8
0
Modern Web

不只是登入畫面!一起打造現代化登入系統系列 第 8

房門與門鎖[ 5 / 6 ]:OAuth 實戰 — 用 Firebase 實作 Google 登入

  • 分享至 

  • xImage
  •  

在上一篇,我們完成了登入表單的驗證,算是裝好了「基本門鎖」。但如果你常用現代網站或 App,應該會發現:許多平台除了傳統帳號密碼之外,還提供了「社群登入」選項。
今天,我們就來看看怎麼透過 Firebase,快速整合 Google 登入,體驗一下「不用帶鑰匙,用手機一鍵開門」的便利。

Google 社群登入

https://ithelp.ithome.com.tw/upload/images/20250922/20110586CopIwakrdC.png

本篇重點整理:

  • Firebase:介紹它是什麼,並帶你了解 OAuth / OAuth 2.0 的概念
  • 社群登入實作:以 Google 為例,完成初始化並示範登入程式碼

Firebase

Firebase 是 Google 推出的 BaaS (Backend as a Service) 平台,意思就是「把後端交給它代管」。
對前端開發者來說,這就像請了個專業保全公司:你不用自己從頭蓋一棟伺服器大樓,也不用研究一堆複雜的安全規則,Firebase 幫你處理好一切✅。

它的服務很多,舉幾個常見的:

  • Authentication ( 身份驗證 ):提供帳號登入 / 註冊、社群登入、匿名登入等功能
  • Firestore ( 資料庫 ):即時資料庫,方便處理使用者資料或聊天訊息
  • Hosting ( 前端託管 ):幫你部署網站,還能綁定網域
  • Cloud Functions ( 雲端函式 ):讓你寫伺服器邏輯,部署在雲端執行

https://ithelp.ithome.com.tw/upload/images/20250922/20110586PjdGtIpubi.png
在這篇文章裡,我們會聚焦在 Authentication ( 身份驗證 )。因為對我們這次主題 — 「房門與門鎖」來說,這就是幫使用者配置「電子門禁卡」的核心。

為什麼需要 Authentication?

如果系統沒有身份驗證,任何人都能隨便進來亂搞,資料不保,使用體驗也無法個人化。
Firebase Authentication 幫我們解決了這些問題:它提供安全的登入流程、整合各種社群帳號,甚至會自動幫我們管理 Session,不需要自己寫一大堆 Token 保存的程式。


OAuth 與 OAuth 2.0

既然要談社群登入,就不能不提 OAuth。
它的全名是 Open Authorization,顧名思義,就是一套「授權」規範。

想像一下🤔,你要用某個任務管理 App,選擇「用 Google 帳號登入」。
整個過程不會是把 Google 的帳號密碼交給那個 App,而是:

  • 你跳到 Google 的登入頁 → 確認授權 → Google 驗證後回傳一個 授權 Token
  • 那個 App 只知道「這是 Google 認證過的你」,卻永遠拿不到你的密碼。

目前最常見的版本是 OAuth 2.0,它比舊版更安全、彈性更大。像是:用 Token 代替密碼驗證、支援各種應用情境,還能細分授權範圍(只讀 Email、不碰 Google Drive 等)。


Firebase 與 OAuth 的關係

那 Firebase Authentication 呢? 其實它就是幫我們把 OAuth 2.0 這些繁瑣的流程打包好。
本來需要自己寫一堆跳轉、授權、Token 管理的程式碼,現在只要呼叫幾個 API:

  • 使用者點擊「用 Google 登入」
  • Firebase 自動帶去 Google 登入頁
  • 驗證後回傳授權結果,並自動維護 Session

換句話說,Firebase 把「複雜的 OAuth 流程」簡化成幾行程式碼,讓前端工程師能快速導入安全的社群登入功能☺️。


實作 Firebase + Google 登入

講了這麼多,來點實際操作吧!在實作之前,我先提醒一下:

🔰 Firebase 本身是 Google 出品的服務,所以 Google 登入的串接流程算是最簡單的,不需要額外跑去開發者後台申請 API Key,就能直接完成。

如果想要整合其他的登入方式,例如 Meta (Facebook)、X (Twitter)、GitHub,流程就會複雜一點。這些第三方平台都會要求你先到它們的官方開發者平台申請一組 API Key / Secret,再把這些金鑰設定到 Firebase 控制台,最後才能完成串接。

既然如此,我們就先從最簡單的 Google 登入 開始,一步步完成設定!


1. 建立 Firebase 專案

進入 Firebase Console ,登入帳戶並建立一個新專案。
接著在專案裡新增一個網頁應用程式
https://ithelp.ithome.com.tw/upload/images/20250922/201105869EsoNciyEq.png
建立完成後,Firebase 會提供初始化流程與一組專案金鑰。

2. 安裝 Firebase 套件

將 firebase 套件下載至專案中

npm i firebase

3. 初始化 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 讀取。

4. 啟用 Google 登入供應商

最後,回到 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()),
};

上一篇
房門與門鎖[ 4 / 6 ]:表單再升級 — reCAPTCHA 與 zxcvbn.js 驗證
下一篇
房門與門鎖[ 6 / 6 ]:完整流程 — Axios + JSON Server + Toast
系列文
不只是登入畫面!一起打造現代化登入系統9
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言