到了第 8 天,我們的「AI 輔助個人財務追蹤器」已經有了資料庫 ERD 與基礎 API。但記帳軟體最核心的一點就是——隱私。我總不能讓全網際網路的人都看到我昨晚又爆買了什麼吧?
今天我們要開啟 Vibe Mode,讓 AI 幫我們在 15 分鐘內把 Supabase Auth 整合進 Next.js 專案,實現 Email/密碼登入與路由保護(Middleware Protect)!
我們正在開發一個 Next.js 14 (App Router) + Supabase 的個人財務追蹤專案。
請幫我撰寫 Supabase Auth 的初始化程式碼:
1. 包含 Server-side 與 Client-side 的 Supabase client 建立方式 (@supabase/ssr)。
2. 提供一個登入/註冊的 React 組件 (Login Form),使用 Tailwind CSS 美化。
3. 請確保遵循 Next.js App Router 的最佳實踐。
AI 很快地幫我生成了 Client Client 的範例:
// utils/supabase/client.ts
import { createBrowserClient } from '@supabase/ssr'
export function createClient() {
return createBrowserClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
)
}
以及 Login Form 組件的提交邏輯:
// app/login/page.tsx
'use client'
import { useState } from 'react'
import { createClient } from '@/utils/supabase/client'
export default function LoginPage() {
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const supabase = createClient()
const handleSignIn = async () => {
const { error } = await supabase.auth.signInWithPassword({ email, password })
if (error) alert(error.message)
else window.location.href = '/dashboard'
}
// ...UI JSX 略...
}
雖然程式碼看起來能用,但我立刻發現了幾個隱患與過時作法:
window.location.href = '/dashboard',這會導致全頁硬刷(Hard Refresh),失去了 SPA/Next.js Client-side Routing 的優勢。我將其改為 useRouter() 的 router.push('/dashboard') 並搭配 router.refresh()。在接續讓 AI 生成 middleware.ts 來保護敏感路由(例如 /dashboard)時,系統居然陷入了無限重定向的死迴圈!
// middleware.ts
import { NextResponse, type NextRequest } from 'next/server'
import { createServerClient } from '@supabase/ssr'
export async function middleware(request: NextRequest) {
// ... Supabase 初始化 ...
const { data: { user } } = await supabase.auth.getUser()
// 🐛 這裡出事了!沒有檢查目前是否已經在 /login 頁面
if (!user) {
return NextResponse.redirect(new URL('/login', request.url))
}
}
當使用者未登入存取任何頁面時,Middleware 會把他導向 /login;然而當瀏覽器請求 /login 時,Middleware 又觸發了一次,檢查發現依然「未登入」,於是再次導向 /login……蹦!直接炸掉。
沒有選擇自己動手改,而是直接把錯誤訊息與程式碼丟回給 AI 進行「對話式除錯」:
你在 middleware.ts 寫的重定向邏輯導致了 Infinite Redirect Loop。
當未登入的使用者存取 /login 頁面時,也會觸發重新導向到 /login。
請修復這個問題,並確保:
1. 如果未登入且存取受保護路由 (如 /dashboard),導向 /login。
2. 如果「已登入」的使用者存取 /login,自動幫他導向 /dashboard。
import { NextResponse, type NextRequest } from 'next/server'
import { updateSession } from '@/utils/supabase/middleware'
export async function middleware(request: NextRequest) {
const { response, user } = await updateSession(request)
const isLoginPage = request.nextUrl.pathname.startsWith('/login')
const isDashboardPage = request.nextUrl.pathname.startsWith('/dashboard')
// 未登入想進 Dashboard -> 送去 Login
if (!user && isDashboardPage) {
return NextResponse.redirect(new URL('/login', request.url))
}
// 已登入還想進 Login -> 送去 Dashboard
if (user && isLoginPage) {
return NextResponse.redirect(new URL('/dashboard', request.url))
}
return response
}
export const config = {
matcher: ['/dashboard/:path*', '/login'],
}
在今天整合 Auth 的過程中,我們學到了幾個關鍵的 JS / Web 概念:
localStorage,但這容易遭受 XSS 攻擊,且 Server-Side Rendering (SSR) 無法在伺服器端讀取到 localStorage。Supabase @supabase/ssr 套件改用 PKCE 流水線 + HTTP-Only Cookies,讓 Server Component 與 Middleware 能直接讀取 Session,安全性大幅提升!async/await)的。處理 HTTP Header 與 Response 重新轉發時,必須確保 Cookie 有正確寫回 NextResponse,否則使用者登入完重新整理又會被登出。Vibe Coding 不是盲目複製貼上,而是讓 AI 當你的超速打字員,你來當最後的架構審查官。今天順利建立了使用者驗證機制,明天 Day 9 我們將繼續推進,引導 AI 為系統加上嚴密的 Zod 資料驗證與 Try-Catch 邊界處理!