測驗:Supabase Auth 身份驗證完整指南
共 5 題,點選答案後會立即顯示結果
1. Supabase Auth 是基於哪個開源專案建構的?
2. 在 Supabase Auth 的 Session 結構中,access_token 的預設有效期是多久?
3. 使用 supabase.auth.signUp() 註冊新使用者後,Supabase 預設會做什麼?
4. 在 onAuthStateChange 監聽器中,當 Token 自動刷新時會觸發哪個事件?
5. 設定 OAuth 登入時,在 Supabase Dashboard 設定的授權重新導向 URI 格式應該是什麼?
前言
在上一篇文章中,我們認識了 Supabase 的基本架構與專案設定。本篇將深入探討 Supabase Auth,這是 Supabase 提供的身份驗證服務,讓你能快速為應用程式加入使用者管理功能。
讀完本篇後,你將能夠:
- 理解 Supabase Auth 的架構設計
- 實作 Email/Password 註冊登入
- 設定 OAuth 社群登入(Google, GitHub)
- 處理使用者 session 管理
Supabase Auth 架構介紹
Supabase Auth 基於 GoTrue 開源專案建構,提供完整的身份驗證解決方案。讓我們先看看它的核心架構:
┌─────────────────────────────────────────────────────┐
│ 你的應用程式 │
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ 登入表單 │ │ 註冊表單 │ │
│ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │
│ └────────┬─────────┘ │
│ ▼ │
│ ┌───────────────┐ │
│ │ Supabase SDK │ │
│ └───────┬───────┘ │
└─────────────────┼───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ Supabase Auth (GoTrue) │
│ │
│ ┌─────────────────────────────────────────────┐ │
│ │ 認證方法 │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────────┐ │ │
│ │ │ Email/ │ │ OAuth │ │ Magic Link │ │ │
│ │ │Password │ │ Provider│ │ / OTP │ │ │
│ │ └─────────┘ └─────────┘ └─────────────┘ │ │
│ └─────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────┐ │
│ │ Session / Token 管理 │ │
│ │ • Access Token (JWT) │ │
│ │ • Refresh Token │ │
│ │ • 自動刷新機制 │ │
│ └─────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────┐ │
│ │ auth.users 資料表 │ │
│ │ • id (UUID) │ │
│ │ • email │ │
│ │ • created_at │ │
│ │ • user_metadata │ │
│ └─────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────┘
關鍵概念
當你在專案中使用 Supabase Auth 時,會遇到以下幾個核心元件:
1. auth.users 資料表
這是 Supabase 自動建立的系統資料表,儲存所有使用者資訊:
-- 這是 Supabase 自動管理的,你不需要手動建立
-- 但了解其結構有助於理解 Auth 運作方式
-- auth.users 主要欄位
id -- UUID,使用者唯一識別碼
email -- 電子郵件
encrypted_password -- 加密後的密碼
email_confirmed_at -- 郵件驗證時間
created_at -- 建立時間
updated_at -- 更新時間
raw_user_meta_data -- 自訂使用者資料(JSON)
Code language: JavaScript (javascript)2. Session 與 Token
Supabase Auth 使用 JWT (JSON Web Token) 管理使用者 session:
// 登入成功後,你會得到這樣的 session 物件
{
access_token: "eyJhbG...", // JWT,有效期較短(預設 1 小時)
refresh_token: "v1.Mxyz...", // 用於刷新 access_token
expires_in: 3600, // access_token 有效秒數
token_type: "bearer",
user: {
id: "123e4567-e89b...",
email: "[email protected]",
// ...其他使用者資訊
}
}
Code language: JavaScript (javascript)Email/Password 驗證流程
最基本的身份驗證方式就是 Email/Password。讓我們看看完整的實作流程。
註冊新使用者
import { createClient } from '@supabase/supabase-js'
const supabase = createClient(
'https://your-project.supabase.co',
'your-anon-key'
)
// 註冊新使用者
async function signUp(email, password) {
const { data, error } = await supabase.auth.signUp({
email: email,
password: password,
options: {
// 可選:附加使用者資料
data: {
display_name: 'John Doe',
avatar_url: 'https://example.com/avatar.png'
}
}
})
if (error) {
console.error('註冊失敗:', error.message)
return null
}
console.log('註冊成功,請檢查郵件確認信箱')
return data.user
}
Code language: JavaScript (javascript)當你呼叫 signUp() 後,Supabase 會:
- 在
auth.users建立一筆新記錄 - 發送驗證郵件到使用者信箱(預設行為)
- 回傳 user 物件(但 session 可能為 null,視設定而定)
登入
// Email/Password 登入
async function signIn(email, password) {
const { data, error } = await supabase.auth.signInWithPassword({
email: email,
password: password
})
if (error) {
console.error('登入失敗:', error.message)
return null
}
// 登入成功,data 包含 user 和 session
console.log('登入成功')
console.log('使用者:', data.user.email)
console.log('Session 有效至:', new Date(data.session.expires_at * 1000))
return data
}
Code language: JavaScript (javascript)取得當前使用者
// 取得當前登入的使用者
async function getCurrentUser() {
const { data: { user }, error } = await supabase.auth.getUser()
if (error || !user) {
console.log('使用者未登入')
return null
}
return user
}
// 取得當前 session
async function getSession() {
const { data: { session }, error } = await supabase.auth.getSession()
if (error || !session) {
console.log('沒有有效的 session')
return null
}
return session
}
Code language: JavaScript (javascript)設定 OAuth Provider
OAuth 讓使用者能用現有的 Google、GitHub 等帳號登入,不需要另外記密碼。以下以 Google OAuth 為例說明設定流程。
步驟一:在 Google Cloud Console 建立 OAuth 憑證
- 前往 Google Cloud Console
- 建立或選擇專案
- 進入「API 和服務」>「憑證」
- 點擊「建立憑證」>「OAuth 用戶端 ID」
- 應用程式類型選擇「網頁應用程式」
- 設定授權重新導向 URI:
- 記下 Client ID 和 Client Secret
步驟二:在 Supabase Dashboard 設定
- 進入 Supabase Dashboard > Authentication > Providers
- 找到 Google,點擊啟用
- 填入剛才取得的 Client ID 和 Client Secret
- 儲存設定
步驟三:實作 OAuth 登入
// Google OAuth 登入
async function signInWithGoogle() {
const { data, error } = await supabase.auth.signInWithOAuth({
provider: 'google',
options: {
// 登入成功後重導向的 URL
redirectTo: 'http://localhost:3000/auth/callback',
// 可選:要求的權限範圍
scopes: 'email profile'
}
})
if (error) {
console.error('Google 登入失敗:', error.message)
return
}
// OAuth 會重導向到 Google 登入頁面
// 成功後會導回 redirectTo 指定的 URL
}
// GitHub OAuth 登入(設定方式類似)
async function signInWithGitHub() {
const { data, error } = await supabase.auth.signInWithOAuth({
provider: 'github',
options: {
redirectTo: 'http://localhost:3000/auth/callback'
}
})
if (error) {
console.error('GitHub 登入失敗:', error.message)
}
}
Code language: JavaScript (javascript)OAuth 回調處理
當使用者從 OAuth Provider 登入成功後,會被重導向回你的應用程式。你需要處理這個回調:
// 在你的 callback 頁面(例如 /auth/callback)
async function handleAuthCallback() {
// Supabase SDK 會自動從 URL 解析並設定 session
const { data: { session }, error } = await supabase.auth.getSession()
if (error) {
console.error('驗證回調錯誤:', error.message)
// 重導向到登入頁面
window.location.href = '/login?error=auth_failed'
return
}
if (session) {
console.log('OAuth 登入成功')
// 重導向到主頁面
window.location.href = '/dashboard'
}
}
Code language: JavaScript (javascript)Session 管理與 Token 處理
理解 session 管理是使用 Supabase Auth 的關鍵。
Session 生命週期
使用者登入
│
▼
┌─────────────────────────────────────────────────┐
│ 發放 Session │
│ • access_token (有效期 1 小時) │
│ • refresh_token (有效期較長) │
└───────────────────────┬─────────────────────────┘
│
▼
┌─────────────────────────────────────────────────┐
│ 正常使用期間 │
│ • 每個 API 請求帶上 access_token │
│ • SDK 自動處理 token 附加 │
└───────────────────────┬─────────────────────────┘
│
access_token 即將過期
│
▼
┌─────────────────────────────────────────────────┐
│ 自動刷新 (SDK 自動處理) │
│ • 使用 refresh_token 取得新的 access_token │
│ • 對使用者透明,無感知 │
└───────────────────────┬─────────────────────────┘
│
refresh_token 過期 或 使用者登出
│
▼
┌─────────────────────────────────────────────────┐
│ Session 結束 │
│ • 需要重新登入 │
└─────────────────────────────────────────────────┘
手動刷新 Session
雖然 SDK 會自動刷新,但有時你可能需要手動處理:
// 手動刷新 session
async function refreshSession() {
const { data: { session }, error } = await supabase.auth.refreshSession()
if (error) {
console.error('Session 刷新失敗:', error.message)
// 可能需要重新登入
return null
}
console.log('Session 已刷新')
return session
}
Code language: JavaScript (javascript)監聽認證狀態變化 (onAuthStateChange)
onAuthStateChange 是處理認證狀態的核心方法。它讓你能即時反應使用者的登入/登出狀態:
// 設定認證狀態監聽器
function setupAuthListener() {
const { data: { subscription } } = supabase.auth.onAuthStateChange(
(event, session) => {
console.log('認證狀態變化:', event)
switch (event) {
case 'INITIAL_SESSION':
// 初始化時檢查現有 session
if (session) {
console.log('發現現有 session')
} else {
console.log('沒有現有 session')
}
break
case 'SIGNED_IN':
// 使用者登入
console.log('使用者已登入:', session.user.email)
// 更新 UI、導航到主頁面等
break
case 'SIGNED_OUT':
// 使用者登出
console.log('使用者已登出')
// 清除本地狀態、導航到登入頁面等
break
case 'TOKEN_REFRESHED':
// Token 已刷新
console.log('Token 已自動刷新')
break
case 'PASSWORD_RECOVERY':
// 密碼重設流程
console.log('密碼重設流程中')
// 顯示密碼重設表單
break
case 'USER_UPDATED':
// 使用者資料已更新
console.log('使用者資料已更新')
break
}
}
)
// 記得在元件卸載時取消訂閱
// subscription.unsubscribe()
return subscription
}
Code language: JavaScript (javascript)在 React 中使用
import { useEffect, useState } from 'react'
function App() {
const [user, setUser] = useState(null)
const [loading, setLoading] = useState(true)
useEffect(() => {
// 取得初始 session
supabase.auth.getSession().then(({ data: { session } }) => {
setUser(session?.user ?? null)
setLoading(false)
})
// 監聽狀態變化
const { data: { subscription } } = supabase.auth.onAuthStateChange(
(event, session) => {
setUser(session?.user ?? null)
}
)
// 清理
return () => subscription.unsubscribe()
}, [])
if (loading) {
return <div>載入中...</div>
}
return user ? <Dashboard user={user} /> : <LoginPage />
}
Code language: JavaScript (javascript)登出與密碼重設功能
登出
// 登出
async function signOut() {
const { error } = await supabase.auth.signOut()
if (error) {
console.error('登出失敗:', error.message)
return false
}
console.log('已登出')
return true
}
// 在所有裝置上登出(撤銷所有 session)
async function signOutEverywhere() {
const { error } = await supabase.auth.signOut({
scope: 'global' // 撤銷所有 session
})
if (error) {
console.error('全域登出失敗:', error.message)
return false
}
return true
}
Code language: JavaScript (javascript)密碼重設
密碼重設分為兩個步驟:發送重設郵件和設定新密碼。
// 步驟一:發送密碼重設郵件
async function sendPasswordReset(email) {
const { error } = await supabase.auth.resetPasswordForEmail(email, {
// 重設連結點擊後導向的 URL
redirectTo: 'http://localhost:3000/reset-password'
})
if (error) {
console.error('發送重設郵件失敗:', error.message)
return false
}
console.log('密碼重設郵件已發送')
return true
}
// 步驟二:在重設頁面設定新密碼
// 當使用者點擊郵件中的連結時,會導向你的 reset-password 頁面
// Supabase 會自動處理 URL 中的 token
async function updatePassword(newPassword) {
const { data, error } = await supabase.auth.updateUser({
password: newPassword
})
if (error) {
console.error('更新密碼失敗:', error.message)
return false
}
console.log('密碼已更新')
return true
}
Code language: JavaScript (javascript)常見錯誤處理
使用 Supabase Auth 時,你會遇到各種錯誤。以下是常見錯誤及處理方式:
// 統一的錯誤處理函式
function handleAuthError(error) {
// 錯誤物件結構
// {
// message: "錯誤訊息",
// status: 400, // HTTP 狀態碼
// name: "AuthApiError"
// }
const errorMessages = {
// 註冊相關
'User already registered': '此 Email 已被註冊',
'Password should be at least 6 characters': '密碼至少需要 6 個字元',
// 登入相關
'Invalid login credentials': 'Email 或密碼錯誤',
'Email not confirmed': '請先驗證您的 Email',
// Session 相關
'JWT expired': '登入已過期,請重新登入',
'Invalid Refresh Token': '登入憑證無效,請重新登入',
// OAuth 相關
'OAuth callback error': '第三方登入失敗',
// 通用
'Rate limit exceeded': '請求太頻繁,請稍後再試'
}
// 找到對應的中文訊息,或使用原始訊息
const friendlyMessage = errorMessages[error.message] || error.message
console.error(`Auth Error [${error.status}]:`, friendlyMessage)
return friendlyMessage
}
// 使用範例
async function safeSignIn(email, password) {
const { data, error } = await supabase.auth.signInWithPassword({
email,
password
})
if (error) {
const message = handleAuthError(error)
// 顯示給使用者
alert(message)
return null
}
return data
}
Code language: JavaScript (javascript)常見錯誤情境
| 錯誤訊息 | 原因 | 解決方式 |
|---|---|---|
| User already registered | Email 已被使用 | 提示使用者登入或重設密碼 |
| Invalid login credentials | 帳密錯誤 | 提示重新輸入 |
| Email not confirmed | 未驗證 Email | 提示檢查郵件或重發驗證信 |
| JWT expired | Token 過期 | SDK 通常自動處理,否則重新登入 |
| Rate limit exceeded | 請求過於頻繁 | 加入延遲或防抖機制 |
小結
本篇涵蓋了 Supabase Auth 的核心功能:
- 架構理解:Supabase Auth 基於 GoTrue,使用 JWT 管理 session
- Email/Password 驗證:signUp、signIn、getUser 等基本操作
- OAuth 設定:Google、GitHub 等第三方登入的設定與實作
- Session 管理:Token 生命週期與自動刷新機制
- 狀態監聽:onAuthStateChange 即時追蹤認證狀態
- 輔助功能:登出與密碼重設
- 錯誤處理:常見錯誤的識別與友善提示
下一篇我們將深入 Supabase Database,學習如何使用 PostgreSQL 資料庫並結合 Row Level Security (RLS) 保護資料安全。
延伸閱讀
進階測驗:Supabase Auth 身份驗證完整指南
共 5 題,包含情境題與錯誤診斷題。
1. 你正在開發一個 React 應用程式,需要在使用者登入狀態改變時更新 UI。你希望在元件載入時取得現有 session,並持續監聽認證狀態變化。最佳的實作方式是? 情境題
2. 你的應用程式需要支援 Google OAuth 登入。你已經在 Google Cloud Console 建立了 OAuth 憑證,接下來在 Supabase Dashboard 設定完成後,前端應該如何呼叫登入? 情境題
3. 小華在實作登入功能時遇到錯誤,以下是他的程式碼和錯誤訊息: 錯誤診斷
這個錯誤最可能發生在什麼情況?