別再自己從頭刻 Login 系統了!
如果你曾經在全端專案裡自己用 Node.js 寫過會員註冊登入系統,你一定知道那有多痛苦。 你需要把密碼加上鹽巴 (Salt) 用 bcrypt 雜湊、發送 JWT Token、處理 Token 過期與 Refresh Token、實作「忘記密碼」發送信件的邏輯... 更別提還有煩死人的 OAuth (Google/GitHub 登入)。 而且一旦你寫錯一個環節,整個系統的會員資料就有可能被駭客整包端走。
在 2026 年的現代全端開發中,專業的工程師不會去重複造輪子。我們會使用 BASS (Backend as a Service) 服務。而 Supabase 就是我們 Vibe Tutor 萬中選一的超級大腦。
為什麼選擇 Supabase?
很多人會拿 Firebase 來比較。Firebase 雖然好用,但它底層是 NoSQL 資料庫,對於我們這種需要嚴謹關聯「會員」與「多筆訂單」的電商/知識付費系統來說,關聯式資料庫 (RDBMS) 絕對是更安全的選擇。 Supabase 底層是地球上最強大的開源關聯式資料庫 PostgreSQL。它不僅提供了極速的 API,更內建了一套完美無缺的 Auth (身份驗證) 系統。
在 Next.js 15 中實作 Supabase Auth
在 Next.js 的 App Router 架構下,因為環境被切分為 Server 端與 Client 端,所以我們必須準備兩種 Supabase Client 來應對。
這一切都已經在我們專案的 src/utils/supabase/ 資料夾中設定好了。
1. client.ts (前端瀏覽器專用)
這主要用在有 "use client" 的元件中,例如你的登入表單元件。
import { createBrowserClient } from '@supabase/ssr'
export function createClient() {
return createBrowserClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
)
}
2. server.ts (後端伺服器專用)
這用在 Server Components 或是 API Routes 中,用來在後端驗證使用者的身份。它需要處理 Cookie 的讀取與寫入。
import { createServerClient } from '@supabase/ssr'
import { cookies } from 'next/headers'
export async function createClient() {
const cookieStore = await cookies()
return createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
getAll() {
return cookieStore.getAll()
},
setAll(cookiesToSet) {
try {
cookiesToSet.forEach(({ name, value, options }) => {
cookieStore.set(name, value, options)
})
} catch (error) {
// 在 Server Component 中,有時候不能直接設 cookie,這裡捕捉例外
}
},
},
}
)
}
Email 魔法連結 (Magic Link) 登入實戰
在 Vibe Tutor 的登入頁面 (src/app/login/page.tsx) 中,我們採用了目前最流行、無摩擦的 Magic Link 登入方式。
使用者不需要設定密碼,也不需要記住密碼。只要輸入 Email,系統就會寄一封帶有安全連結的信件給他,點擊即可瞬間登入。
看看這段處理登入的核心邏輯:
"use client";
import { createClient } from "@/utils/supabase/client";
const handleLogin = async (e) => {
e.preventDefault();
const supabase = createClient();
// 呼叫 Supabase 的 signInWithOtp 方法發送魔法連結
const { error } = await supabase.auth.signInWithOtp({
email,
options: {
// 登入成功後,重新導向到會員中心
emailRedirectTo: `${location.origin}/auth/callback?next=/dashboard`,
},
});
if (error) {
alert("寄送信件失敗,請稍後再試。");
} else {
alert("魔法登入連結已寄送至您的信箱!請前往收信。");
}
};
就這麼簡單!我們完全不需要去處理寄送 Email 的 SMTP 伺服器設定,Supabase 內部全部幫你打理好了。
Auth Callback 處理機制
當使用者在信箱中點擊了魔法連結,這是一個長得像 https://你的網站.com/auth/callback?code=xxx 的網址。
我們必須在 src/app/auth/callback/route.ts 這個 API Route 攔截它,並將 URL 上的 code 換成真實的登入憑證 (Session Cookie)。
import { NextResponse } from 'next/server'
import { createClient } from '@/utils/supabase/server'
export async function GET(request: Request) {
const { searchParams, origin } = new URL(request.url)
const code = searchParams.get('code')
const next = searchParams.get('next') ?? '/dashboard' // 預設跳轉到會員中心
if (code) {
const supabase = await createClient()
// 將 code 交換成使用者的登入 Session
const { error } = await supabase.auth.exchangeCodeForSession(code)
if (!error) {
return NextResponse.redirect(`${origin}${next}`)
}
}
// 驗證失敗的處理
return NextResponse.redirect(`${origin}/login?error=InvalidToken`)
}
這個交換過程完成後,瀏覽器中就會寫入具有 HttpOnly 屬性的安全 Cookie。
從此以後,當使用者瀏覽任何一頁,我們都能在 Server 端透過 await supabase.auth.getUser() 瞬間認出他是誰,並讀取他的訂單權限。
掌握了強大且優雅的 Supabase 身份驗證,接下來,我們將進入系統的核心心臟:PostgreSQL 資料庫設計,看看我們是如何規劃那張神秘的 vt_purchases 訂單資料表的。