- 使用 Supabase 身份驗證與 Google 登入功能建立登入與註冊流程,以控制應用程式的存取權限
- 在您的 Express.js 後端新增驗證中介軟體,以保護 API 路由免於未經授權的請求
- 測試前端與後端安全性,以驗證僅登入用戶能觸發您的AI功能
- 註冊與登入頁面,用於建立使用者帳戶並維持使用者登入狀態
- 受保護的 API 路由,配備 後端中介軟體,在處理請求前會驗證身份驗證令牌
學生們已在 GitHub 儲存庫中建置出一個經過除錯的最小可行產品(MVP),該產品包含 React 前端與 Express.js 後端。前端與後端可在 Codespaces 中作為兩個獨立的程序執行。
透過驗證保護您的應用程式
目前,任何持有您的 Codespace 連結者皆可使用您的應用程式。系統未設置登入畫面,且您的 Express.js 後端會無條件處理所有接收到的請求。若有人發現您的網址,便能觸發耗費您金錢的 AI 調用。正如您在制定財務計畫時所探討的,每次 API 調用皆伴隨成本。保護這些調用不僅是技術決策,更是商業決策。
在本單元中,我們將以TutorMatch作為引導範例。您將先參照 TutorMatch 的範例,再將每個步驟調整應用於您自己的最小可行產品(MVP)。
本裝置增加了兩層防護。
- 使用者驗證——一個登入畫面,確保僅有註冊使用者能查看您的應用程式。
- 安全檢查 在您的後端系統中,確保每項 API 請求確實來自已登入的使用者。
試想:登入頁面如同守護前門,後端安全檢查則如同守護窗戶。兩者相輔相成,意味著您的應用程式獲得了雙重防護。
為何不自己儲存密碼?
最直觀的用戶帳戶管理方式也是最危險的:建立一個用戶資料表,在用戶註冊時儲存其電子郵件和密碼,並在用戶登入時驗證密碼。
問題在於,此方法將密碼以純文字形式儲存,任何存取資料庫的人都能讀取。一旦資料庫遭外洩,所有使用者的密碼都將暴露無遺。專業級應用程式絕不會採用這種做法。
安全的驗證需要多項要素協同運作:
- 密碼雜湊化 – 將密碼轉換為不可讀取的字串後再進行儲存
- 會話代幣 – 臨時識別碼,讓使用者無需在每個頁面輸入密碼即可保持登入狀態
- 安全儲存 –憑證採用加密處理,確保其在傳輸中或靜止狀態下絕不外洩。
從頭開始建構所有這些內容既複雜又容易出錯。正因如此,驗證服務應運而生——它們負責處理安全性,讓您能專注於應用程式開發。
選擇用戶認證服務時,有以下幾種選項:
選項
運作原理
免費方案
複雜性
判決
自行儲存密碼
建立使用者資料表、對密碼進行雜湊處理、手動管理工作階段
自由的
高——安全失誤既容易發生又危險
不建議用於MVP
Google 登入 (OAuth)
使用者點擊「使用 Google 登入」。Google 負責處理所有安全事宜。
自由的
中階 — 需設定 Google Cloud Console
絕佳選擇,但需增加設定步驟
Auth0 / 職員
具備預建登入頁面的第三方認證服務
免費方案可用
Medium — 需新帳號及 SDK
不錯,但又增加了一項需要管理的服務
Supabase 身份驗證
內建於 Supabase。自動處理雜湊、會話及儲存功能。
免費(已包含)
低 — 您已擁有一個 Supabase 帳戶
MVP 的絕佳選擇
本課程將採用 Supabase Auth 進行用戶驗證。我們還會添加 Google 登入功能,因其是專業應用程式的標準登入選項,且設定難度不高。選擇 Supabase Auth 作為主要方案基於以下三點考量:
- 您已透過創辦人工具包取得 Supabase 帳戶。
- Supabase 開箱即用,即可處理上述所有複雜的安全性問題。
- 免費方案涵蓋您打造最小可行產品所需的一切功能。
還有一點需要考慮:Supabase Auth支援行級安全性(RLS), 這是一種可透過設定資料庫 規則來過濾資料的功能,確保每位使用者僅能查看自身記錄。我們在將 Supabase 加入創辦人工具包時曾簡要提及此功能。然而,RLS 無法保護您的 AI 路由,因此我們需要追加額外的防護措施。
目前階段無需使用 RLS,因為我們並未建立任何自訂資料表。Supabase 會內部處理使用者儲存機制。若您在 MVP 中儲存使用者專屬資料(例如保存使用者的歷史紀錄、偏好設定或交易紀錄),此時 RLS 才顯得重要。
Supabase 驗證
當您建立 Supabase 專案時,驗證功能會自動啟用。您無需在 Supabase 設定中進行任何特殊配置。我們已在創始人工具包中針對匿名使用者與團隊成員設定了特定的 RLS 安全策略,但此資料庫將採用 Supabase 預設的內建驗證機制。
Supabase 自動配置的項目:
- 電子郵件/密碼驗證功能已啟用
- 使用者資料表 (auth.users) 已建立
- 已發行的 JSON 網路憑證 (JWT)
- 會話管理已處理
在第一個活動中,您將為最小可行產品(MVP)添加登入與註冊流程,確保僅限註冊用戶能使用您的應用程式。我們將逐步完成一系列提示操作,每次完成後都會進行測試,確認功能正常運作後再繼續推進。
本課活動中的每個提示皆採用 角色 + 任務 結構。這是值得練習的提示工程模式:
- 角色 告訴人工智慧該成為哪種專家
- 任務 精確地告訴它該做什麼。
將這兩者分開能使您的提示更清晰,並產生更佳的結果,尤其在使用較小的AI模型時。
活動 1
使用 Supabase 添加使用者驗證
預估時間:30分鐘
您將更新應用程式,要求使用者登入後方可使用。新用戶可註冊帳號,回訪用戶可登入,而未持有帳號者將被導向登入頁面。
1. 在您的 GitHub 儲存庫中開啟新分支
- 前往 GitHub 並開啟您的 MVP 儲存庫。
- 建立一個名為add-auth 的新分支。
- 在新的分支上開啟您的 Codespace。
您即將進行重大變更——功能分支確保主分支安全無虞。僅在完成測試後,才會將變更合併至主分支。
2. 在 Supabase 中建立新專案
- 前往 Supabase。您應已透過創辦人工具包取得帳戶與專案。
- 建立一個新專案。為您的最小可行產品(MVP)取一個合適的名稱。
- 您需要建立一個資料庫密碼。請建立密碼並將其存放在安全處,因為一旦儲存後便無法再查看該密碼。
- 設定驗證設定:
- 前往您的Supabase 儀表板 → 驗證 → 設定
- 向下滾動至登入/供應商區段
- 啟用電子郵件確認功能
- 將其關閉(用於測試)
- 切記在進入生產環境時重新開啟它!
- 取得您的專案網址與 API 金鑰
- 在左側邊欄中,點擊「專案設定」(底部齒輪圖示)
- 點擊資料 API。
- 您將看到API 網址。請複製並將其儲存至電腦中的某個位置。您的應用程式將需要此網址。
- 返回專案設定選單,點擊API 金鑰。
- 您將看到兩組金鑰:可發佈金鑰與私密金鑰。
- 複製可發佈金鑰。您的應用程式也需要此金鑰。
公開網址與可發佈金鑰皆為開放狀態,可直接用於應用程式的前端。由於 Supabase 採用 RLS(資料列層級安全性)機制,資料受到保護。使用者可註冊及登入,但僅能檢視自身資料,且不具備管理員權限。
我們在此不會使用私密金鑰。該金鑰絕不應在前端使用。我們將在後續處理付款流程時使用它。
3. 將 Supabase 網址與 API 金鑰新增至 .env 檔案
- 在您的專案中,新增一個 .env 檔案於您的 根目錄。您已存在一個 .env 檔案。 server 目錄中,但由於 Supabase 整合發生於前端,因此該檔案需存在於專案的根目錄中。
- 在您的根目錄下新增兩行 .env 檔案中 檔案中新增兩行:
VITE_SUPABASE_URL=您的公開網址
VITE_SUPABASE_KEY=您的發佈金鑰 - 請將您的實際 Supabase 網址與可發佈金鑰加入檔案中的這些行。
注意:我們使用的是 React,因此需要在前端環境變數前加上 VITE_ 前綴。您的框架可能有所不同。
4. 安裝 Supabase 客戶端
在您的 Codespace 終端機中輸入:
npm install @supabase/supabase-js
5. 提示 Copilot 建立 Supabase 註冊表單並處理
請提示 Copilot(或其他大型語言模型)協助生成您應用程式的登入/註冊代碼。以下為提示範例:
角色:您是一位網頁開發者,協助我在 React 應用程式中實作使用者驗證功能。
任務:新增 Supabase 註冊與登入表單,並與 Supabase 網址進行互動。開啟畫面為登入頁面,使用者登入後將顯示其他標籤頁,並可存取該兩個標籤頁。登入表單應以 Authform.jsx 檔案形式新增至 Pages 資料夾。 當使用者登入後,需在其他標籤頁按鈕旁新增登出按鈕,以便使用者能重新登出並返回主畫面。
請為「授權表單」採用與「尋找導師」及「獲取建議」標籤頁相同的格式設定。
我已將 Supabase 專案網址與可發佈的 API 金鑰新增至 .env 檔案中,分別設定為 VITE_SUPABASE_URL 和 VITE_SUPABASE_URL。
6. 測試驗證
測試完整流程:
- 註冊:
- 前往 /signup
- 輸入電子郵件和密碼(至少6個字元)
- 點擊「註冊」
- 應重定向至首頁
- 在 Supabase 中驗證:
- 開啟 Supabase 儀表板 → 驗證 → 使用者
- 您應該會看到您的新使用者列在其中
- 登出:
- 點擊登出按鈕
- 應重定向至登入頁面
- 登入:
- 請輸入相同的電子郵件/密碼
- 應重定向至首頁
- 會話持久性:
- 重新整理頁面
- 仍應保持登入狀態
若任何步驟失敗,請使用「教我」模式與 Copilot 互動:描述您預期的結果、實際發生的狀況,並要求 Copilot 在提供修正方案前先說明原因。
請保持 Codespace 和分支開啟,以便進行下一項活動。
雖然添加Google登入選項並非強制要求,但您可免費實現此功能,這將為您的應用程式增添專業氣息。
活動 2
(可選)新增 Google 登入功能
預估時間:30分鐘
1. 在 Supabase 中啟用 Google 登入功能
- 前往您的Supabase 儀表板 → 驗證 → 提供者。
- 在清單中找到Google並啟用它。
- 尋找回呼網址並複製它。通常格式如下:https://[您的專案ID].supabase.co/auth/v1/callback。
2. 在 Google Cloud Console 中建立專案
重要提示:請忽略 畫面頂端的大型「免費試用」或「啟用 300 美元信用額度」橫幅廣告。若點擊這些按鈕,Google將要求您提供信用卡進行身分驗證。
您無需擁有 300 美元的信用額度或「帳單帳戶」即可使用身分識別/驗證 API。
前往Google Cloud 控制台。您 應已透過使用 Google AI Studio 取得帳戶。
建立一個新專案並為其命名。 Google允許每個帳戶建立多個免費專案,無需付費或使用信用卡。
前往API 與服務 → OAuth 同意畫面。
- 點擊「開始使用」。
- 請填寫您的應用程式名稱及用戶支援電子郵件。
- 點擊 外部 為觀眾。
重要提示:僅需提供「應用程式名稱」與「支援電子郵件」。若系統要求您添加標誌或敏感「權限範圍」(例如讀取用戶整個雲端硬碟),Google 可能會要求您使用信用卡進行驗證。
MVP專業提示:只要維持「測試」模式或用戶數量較少,目前無需提交驗證申請。
3. 產生 OAuth 憑證
- 前往API 與服務 → 憑證。
- 點擊+ 建立憑證 → OAuth 客戶端 ID。
- 應用程式類型:請選擇網頁應用程式。
- 授權重定向 URI:貼上您從 Supabase 複製的回呼網址。
- 點擊「建立」。您將收到客戶端識別碼與客戶端密鑰。
- 請務必複製這些內容,因為關閉視窗後將無法再存取它們。您可將其下載為 JSON 格式,如此便能同時保留兩種格式供您使用。
4. 在 Supabase 中啟用供應商
- 返回Supabase 儀表板 → 驗證 → 服務供應商 → Google。
- 請貼上您的客戶識別碼與客戶密鑰。
- 將「啟用 Google 服務提供者」切換至開啟狀態,然後點擊「儲存」。
5. 在 Supabase 中將您的 Github Codespace 網址新增為重定向網址
當使用者使用 Google 登入時,系統需要知道驗證完成後應返回何處。預設會跳轉至http://localhost:3000/,但我們的前端服務實際運行於http://localhost:5173。
- 為確保功能正常運作,請在應用程式執行時複製 GitHub 的網址(格式類似https://codespace_name.app.github.dev/)。
- 在 Supabase 中,前往「驗證」→「URL 設定」。
- 將您的 Github 網址新增為重定向網址。
6. 將網址加入 .env 檔案
- 在您的 .env 中新增 Github Codespace 的網址作為變數VITE_DEVELOPMENT_URL(僅當您的應用程式為 React 時需使用 VITE)。
7. 提示 Copilot 為您的應用程式新增 Google 登入功能
您將要求 Copilot 添加 Google 登入功能。請注意,我們正在添加包含重定向網址的指令。
- 請提示 Copilot 在您的登入畫面中新增 Google 登入作為選項。
角色:您是一位網頁開發者,協助我在 React 應用程式中實作使用者驗證功能。
任務:在登入頁面新增 Google 登入選項。使用 Supabase 的 signInWithOAuth 方法並指定 'Google' 供應商。重定向至環境變數 VITE_DEVELOPMENT_URL。
注意:若您切換 代碼空間,則需建立新的 .env檔案,其中需包含您的重定向設定(以及前端所需的 Supabase URL 與 API 金鑰)。
當您部署應用程式時,必須在 Supabase 中將您的生產環境應用程式網址設定為重定向,同時也需在程式碼中將其設定為重定向。
9. 將您的工作提交至 GitHub
- 將您的變更提交至您的分支。
請保持分支與代碼空間開啟,以便進行下一項活動。
保護您的 API 路由
您的應用程式現已具備登入頁面,因此使用者必須登入才能瀏覽應用程式內容。然而,您的 Express.js 後端伺服器仍會處理所有接收到的請求,即使使用者並非透過前端介面登入。這意味著有人可能直接開啟瀏覽器控制台,繞過登入頁面直接呼叫您的 API。
這是「視窗」問題。您用登入頁面鎖住了前門,但視窗(您的 API 路由)仍敞開著。要解決此問題,您需要新增 安全檢查 (稱為 中間件)至後端,用以驗證每個 API 請求確實來自已登入的使用者。
您需要:
保護以下路徑:
- 花費金錢(AI呼叫、API使用)
- 修改資料(建立、更新、刪除)
- 顯示使用者專屬資訊
保持公開:
- 健康檢查
- 靜態資產
- 首頁(若對公眾可見)
當您的後端接收到請求時:
- 檢查:「此請求是否附有用戶已登入的證明?」
- 若為是→ 正常處理請求
- 若無→ 以「401 未授權」錯誤拒絕請求
該「證明」為 JWT(JSON網路憑證),這是 Supabase 賦予每位登入用戶的長字元串。您的前端在每次向後端發送請求時都會包含此憑證。後端會將憑證提交至 Supabase 進行驗證,以確認其真實性與有效期限。
內容物:
- 使用者身分(其使用者ID、電子郵件)
- 當它被發布時
- 當它到期時
- 一份加密簽名,用以證明該簽名由 Supabase 發出且未遭篡改
活動 3
保護您的 API 路由
預估時間:25分鐘
1. 開啟 Github Codespaces
若您尚未進入 Codespace,請開啟它,並使用與活動 1 和 2 相同的分支。
2. 在您的後端安裝 Supabase
- 在你的 伺服器 資料夾(您的 Express 後端程式所在位置),安裝 Supabase:
- 在終端機視窗中輸入:
cd server
npm install @supabase/supabase-js
- 在終端機視窗中輸入:
3. 添加後端環境變數
- 返回您的 Supabase 專案,並取得 秘密 API 金鑰 (非可發布密鑰)。
- 前往專案設定 → API 金鑰
- 向下滾動至「秘密金鑰」。
- 複製它並將其儲存到電腦上的安全位置。
- 更新 .env 檔案 伺服器 目錄中的 server/.env 是後端專屬的獨立檔案)。
- 請添加以下行,並貼上您的 Supabase 網址與密鑰:
SUPABASE_URL=您的_公開網址
SUPABASE_SERVICE_ROLE_KEY=您的_密鑰
重要:
- 請使用與前端相同的SUPABASE_URL
- 請使用機密金鑰(非公開金鑰)作為SUPABASE_SERVICE_ROLE_KEY
- 私密金鑰具備管理員權限——切勿在前端程式碼中暴露此金鑰
4. 為您的 Express 後端新增驗證中介軟體
- 使用此提示要求 Copilot 在後端程式碼中添加程式碼,以確保 API 呼叫的安全性。
角色:您是協助我強化 Express.js API 安全性後端開發人員。
任務:
- 導入 Supabase 客戶端函式庫 (@supabase/supabase-js)
- 使用環境變數中的 SUPABASE_URL 和 SUPABASE_SERVICE_ROLE_KEY 初始化 Supabase 客戶端。
- 在我的 Express.js 後端中添加一個驗證中間件函數。
- 提取代幣
- 請 Supabase 驗證代幣
- 若為是 → 讓請求繼續
- 若無 → 返回 401 錯誤
僅將此中間件套用至呼叫外部 API 的路由
請勿將其套用於首頁或健康檢查等公開路徑
- 檢視生成的程式碼。
您應能看到一個名為requireAuth(或類似名稱)的函式,該函式會檢查 Authorization 標頭、擷取憑證,並透過 Supabase 進行驗證。您還應看到此函式已套用至您的 API 路由,但未套用至公開路由。
關鍵部分應類似以下程式碼:
async function requireAuth(req, res, next) {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({ error: 'Unauthorized' });
}
const token = authHeader.split(' ')[1];
const { data, error } = await supabase.auth.getUser(token);
if (error || !data.user) {
return res.status(401).json({ error: 'Unauthorized' });
}
req.user = data.user;
next();
}
// Protected route — security check runs first
app.post('/api/ai-query', requireAuth, async (req, res) => {
// This code only runs if the user is logged in
});
// Public route — no security check needed
app.get('/health', (req, res) => {
res.json({ status: 'ok' });
});
對於 TutorMatch 而言,受保護的路由是app.post('/api/ai-query', requireAuth, …)。您的應用程式可能採用不同的路由名稱,但核心模式相同——保護那些執行時會產生費用的路由。
5. 更新前端以傳送代幣
您的後端系統現在要求每次請求都附帶登入驗證,但前端尚未傳送此驗證資訊。
- 請提示 Copilot 更新您的前端程式碼。以下為範例提示語。
角色:您是協助我將 React 應用程式與安全 Express.js 後端連接的前端開發人員。
任務:將所有前端 API 呼叫更新為受保護路由,並在授權標頭中加入 Supabase 會話令牌。
- 查看 Copilot 更新的程式碼:
您會發現現在的fetch呼叫已包含 Authorization 標頭。以下是變更內容:
const { data: { session } } = await supabase.auth.getSession();
if (!session) {
window.location.href = '/login';
return;
}
const response = await fetch('/api/ai-query', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${session.access_token}`
},
body: JSON.stringify({ prompt: userInput })
});
這便是萬物相連之處:
- 當使用者登入時,Supabase Auth 會為您的前端提供一個令牌。
- 您的前端在每次請求時都會傳送該憑證。
- 後端會檢查它。
- 若有效,請求便會通過。
- 否則,它就被封鎖了。
6. 測試雙重安全層級
- 在 Codespaces 中執行您的應用程式,並測試兩種情境:
測試 1 — 已登入使用者(應可正常運作):
- 開啟您的應用程式,並使用測試憑證登入。
- 請如常使用您的AI功能。
- 它應該會和以前完全一樣運作。
如果這能運作,表示您的前端正在傳送憑證,而後端也成功接收到了。
測試 2 — 無登入(應被封鎖):
- 從應用程式登出,使使用者處於未登入狀態。
- 開啟開發人員工具(按 F12 或右鍵點擊檢查),然後點擊「控制台」標籤頁。
- 請嘗試將以下內容貼入控制台直接呼叫您的 API(注意:我們的 擷取路徑為 /api/ai-query – 請將您的擷取指令修改為與前端程式碼中相同的版本。)
fetch('/api/ai-query', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ prompt: 'test' })
}).then(r => r.json()).then(console.log);
You should see { error: “Unauthorized” } — NOT an AI response.
若兩項測試皆通過,您的應用程式即為安全。登入頁面如同守護前門,中間件則如同守護窗戶。
7. 提交與合併
最後檢查一次,確保您的使用者驗證功能正常運作:
- 完全登出並重新整理頁面——您應會看到登入頁面
- 登入並驗證您的 AI 功能是否正常運作
- 開啟開發人員工具控制台,嘗試在不提供憑證的情況下呼叫您的 API(如同測試 2 所示)—— 確認您會收到 401 錯誤。
在源代碼控制系統中提交並確認您的變更,附上訊息如「新增具驗證中介軟體的 API 路由保護」,並將其推送至您的分支。
前往 GitHub,從您的add-auth 分支 建立拉取請求,審閱變更內容,並合併至主分支。
疑難排解
以下是一些可能出現的問題及其解決方案
已登入的使用者會收到 401 錯誤
開啟開發者工具,切換至「網路」分頁,點擊 API 請求項目,檢視「請求標頭」。您應能看到Authorization: Bearer eyJ… 字串。若標頭缺失,請確認您的getSession()呼叫是否正常運作。
API呼叫無需登入即可運作
請確認您已將requireAuth套用至正確的路由。
代幣持續遭到拒絕
請確保您在後端使用的是SUPABASE_SERVICE_ROLE_KEY(而非SUPABASE_ANON_KEY)。服務角色金鑰具備驗證任何使用者所發送令牌的權限。
「supabase 未定義」訊息
請確認已安裝 Supabase 客戶端函式庫(npm install @supabase/supabase-js),並在伺服器檔案頂端導入該函式庫。
反思
添加使用者驗證是讓您的應用程式更專業且安全的關鍵步驟。請考慮以下問題:
安全防護
您的 API 路由
提示
關鍵術語
- 驗證:確認使用者身分——核實其是否為自稱者(註冊與登入)。此概念有別於授權,後者用於管控使用者可存取的範圍。
- 授權:控制經身份驗證的使用者可執行的操作。行級安全性(RLS)是於資料庫層級實現此功能的方式之一。
- 密碼雜湊:將密碼轉換為不可讀取的字串後儲存。Supabase 認證系統會自動執行此步驟。儲存明文密碼將構成嚴重的安全風險。
- 會話代幣:一種臨時識別碼,用於在使用者驗證後維持其登入狀態。Supabase 認證系統會自動管理這些代幣。
- 驗證中介軟體:在路由處理程序執行前運行,用以驗證請求是否來自已登入用戶。若用戶未通過驗證,中介軟體將阻擋該請求。
- 授權標頭:一種HTTP標頭,用於在每次請求中將驗證憑證(通常為代幣)從前端傳遞至後端。
- Bearer Token: A type of authentication token sent in the Authorization header. The format is Bearer <token>. “Bearer” means “whoever carries this token is authorized.”
- JSON Web Token (JWT):一種標準化的驗證憑證格式。內含經編碼處理的用戶資訊及有效期限。Supabase 採用 JWT 進行會話管理。
- OAuth:一種認證 標準,允許使用者透過現有帳戶(如Google)登入,無需建立新密碼。全稱為「開放授權」。
其他資源
- Supabase 身份驗證文件:設定指南、供應商配置與 API 參考
- Supabase Auth Helpers for Express:伺服器端驗證模式
