Supabase Auth 上線指南:登入只是開始,RLS 才是資料邊界
從 session 生命週期、redirect allowlist 到 Row Level Security,建立可驗證且不把 service-role 暴露給瀏覽器的 Supabase Auth。
Supabase Auth 解決的是「這個 request 代表誰」,不是「這個人可以讀哪一列」。真正的資料授權由 Postgres grants 與 Row Level Security(RLS)共同完成。只在 React route 檢查 user 、隱藏按鈕,或把 admin 頁面放在受保護 layout,都屬於使用者體驗層;攻擊者仍可直接呼叫 Data API。 截至 2026-07-29,Supabase 官方文件仍說明 Auth token 會隨 SDK request 傳遞,並可和 RLS 結合成 row-by-row access control。官方也要求 exposed schema(一般為 public )的 table 啟用 RLS。service-role 或 secret key 可具有繞過 RLS 的能力,因此只能存在 server/Edge Function,不可進入 VITE_ 環境變數或瀏覽器 bundle。 實作步驟 第一步建立身份與授權模型。列出匿名使用者、已登入會員、內容作者、客服與 admin 真正需要的操作。把產品 entitlement、團隊 membership、owner ID 放在資料庫可驗證的欄位;不要用可由使用者自行修改的 user metadata 當 admin 依據。Supabase RLS 文件特別區分 raw_user_meta_data 與較適合授權資料的 app metadata,但複雜權限通常仍應落在受保護 table。 第二步選擇 client。純 browser SPA 使用 @supabase/supabase-js 管理 browser session;SSR framework 依官方 server-side 指南使用目前支援的 SSR package,以 Cookie 讓 server 可讀 session,並遵循 PKCE。不要在同一 app 混用多套 storage key 與 refresh owner,否則可能造成重複刷新或登出不一致。 設定 Auth provider、Site URL 與 redirect allowlist 時,development、preview、production 要分開管理。callback 完成後的 next 參數必須套用本地 allowlist。email link 與 OAuth state 要從預期流程產生,不可接受任意外部 redirect。 第三步建立 table 與 constraint,立即啟用 RLS,再加最小 grants/policies。以下範例讓使用者讀寫自己的 profile;真實 schema 應加入 required 欄位、index 與 migration test: create table public.profiles ( id uuid primary key references auth.users(id) on delete cascade, display_name text, created_at timestamptz not null default now() ); alter table public.profiles enable row level security; grant select, update on public.profiles to authenticated; create policy "users read own profile" on public.profiles for select to authenticated using ((select auth.uid()) = id); create policy "users update own profile" on public.profiles for update to authenticated using ((select auth.uid()) = id) with check ((select auth.uid()) = id); USING 篩選既有 rows, WITH CHECK 限制 mutation 後的 row。update 還需要對應 select policy 才能如預期工作。若資料是 public read/private write,分開寫 SELECT 與 mutation policies,不要用一條寬鬆 FOR ALL 省事。 第四步處理 signup profile。可用 database trigger 建立一對一 profile,但 trigger 失敗可能使 signup transaction 失敗;function 應最小、固定 search_path 、有測試與明確錯誤。另一方案是首次登入後用 authenticated upsert 建立 profile,但 policy 必須確保 id = auth.uid() 。不要讓 client 指定另一個 user ID。 第五步建立 server privileged boundary。寄送管理通知、付款入帳或客服查詢可能需要 service-role。把這些操作放在受驗證 Edge Function/server route,先驗證 caller,再以狹窄 RPC 執行。不要把「admin client」物件傳給共用 browser helper,也不要在 error response 回傳 key、JWT 或 database detail。 第六步設計 session lifecycle。監聽登入、登出與 token refresh 以更新 UI,但資料 request 仍要處理 401/403。對敏感操作可要求 recent authentication 或 MFA(依實際方案能力與 threat model)。account deletion、email change、password reset、停權與全裝置登出都要有後端狀態與 audit,不是只清 local storage。 失敗與復原 「登入成功但查不到資料」常是 RLS 正常拒絕,而非 SDK 壞掉。先記錄 request 的角色、user ID 是否存在、table grant 與 policy 條件;不要直接關閉 RLS。用兩個測試帳號證明 A 看不到 B,再測匿名 request。 「所有人都能讀」通常是 table 未啟用 RLS、存在 USING (true) policy、view 以 definer 權限繞過,或 server 把 service-role client 用在一般 request。立即撤除暴露路徑、輪替可能外洩的 secret、啟用/修正 RLS,並從 access log 界定影響。不能只改前端按鈕。 SSR 不斷登出時,檢查 Cookie 是否能由 server adapter 寫回、callback/ middleware 的設定是否相同,以及帶 Set-Cookie 的 response 是否被 CDN cache。回滾時可以暫時回到已驗證的單一 client 流程,但不可將 token 寫入 URL 或 log。 policy migration 導致 production 全拒絕時,使用預先保存的 grants/policies DDL 回滾,而不是停用整張 table 的 RLS。每次 policy change 都要能在 local/staging 以 anon、兩個 authenticated users 與 privileged server role 重播。 驗證指令 可在 local Supabase 或隔離 staging 執行 policy 測試。最低驗收矩陣包含匿名、owner、other user、admin/server 四種角色,對 SELECT/INSERT/UPDATE/DELETE 逐一驗證: select schemaname, tablename, policyname, roles, cmd, qual, with_check from pg_policies where schemaname = 'public' order by tablename, policyname; select relname, relrowsecurity from pg_class join pg_namespace on pg_namespace.oid = pg_class.relnamespace where nspname = 'public' and relkind = 'r'; 再檢查 production build 不含 service-role 變數名或值、redirect allowlist 不接受外部 domain、過期 session 回正確 401、owner 無法更新 owner ID、另一帳號無法存取、登出後 protected request 失效。測試資料與 log 不可含真實個資或 token。 官方來源 Supabase Auth Supabase Server-Side Rendering Supabase Row Level Security Supabase Securing your API 延伸閱讀 從 技術文章 延伸到跨子網域 SSO 與 Edge Function。 在 課程總覽 建立可重跑的 Auth/RLS 測試專案。 發現權限邊界不明確時,可由 聯絡頁 提供去識別化 policy。