ข้ามไปยังเนื้อหา

Sessions and JWTs

sign-in สำเร็จหนึ่งครั้งจะได้ JWT access token อายุสั้นมาพร้อมกับ refresh token อายุยาวกว่า supabase-js จะเก็บและ refresh ทั้งคู่ให้เองแบบเงียบ ๆ และ claim ที่อยู่ใน JWT — user ID กับ role — นี่แหละที่ Postgres ใช้ตัดสินว่า request หนึ่ง ๆ มีสิทธิ์เห็นอะไรบ้าง

ทุกครั้งที่ sign-in สำเร็จจะได้ session ที่มี access_token (เป็น JWT อายุสั้น ปกติประมาณหนึ่งชั่วโมง) และ refresh_token (อายุยาวกว่า ใช้สร้าง access token ใหม่แบบเงียบ ๆ เมื่ออันเก่าหมดอายุ) supabase-js จัดการเรื่อง storage และ refresh cycle ให้ทั้งหมด — คุณไม่ต้องมานั่งตาม expiry เองหรือเรียก refresh endpoint เอง

const {
data: { session },
} = await supabase.auth.getSession();
console.log(session?.access_token); // short-lived JWT
console.log(session?.refresh_token); // used to silently mint a new access token later

ตัว JWT เองพก claim มาด้วย — claim sub เก็บ user ID และ claim role (ปกติเป็น authenticated สำหรับ user ที่ sign-in แล้ว และ anon สำหรับ request ที่ยังไม่ได้ sign-in) ทุก request ที่ supabase-js ยิงไปยัง API ที่ auto-generate จะแนบ JWT นี้ไปด้วย และ Postgres กับ PostgREST จะอ่าน claim พวกนี้ตอนรับ request เข้ามา นี่คือสิ่งที่ทำให้ auth.uid() ทำงานได้ภายใน row level security policy ซึ่งบทถัดไปจะลงลึก — JWT ที่แนบมากับ request คือสิ่งที่ทำให้ policy เช็คได้ว่า row นี้เป็นของ user คนปัจจุบันหรือเปล่า

supabase.auth.getSession() อ่าน session ตรงจาก local storage เลย (refresh ให้ถ้า access token หมดอายุ) จึงคืนค่าทันทีโดยไม่ต้อง round trip ไปหา Auth server ส่วน supabase.auth.getUser() จะส่ง access token ไปให้ Auth server ตรวจสอบใหม่ ดังนั้นช้ากว่าแต่น่าเชื่อถือกว่า — ใช้ getUser() ทุกครั้งที่ต้องการความมั่นใจว่า token ไม่ได้ถูกปลอมหรือถูก revoke ไปแล้ว เช่นก่อนทำ action ฝั่ง server ที่มีความสำคัญสูง

// Fast: reads the locally stored session, refreshing it if needed
const { data: { session } } = await supabase.auth.getSession();
// Authoritative: revalidates the access token against the Auth server
const { data: { user }, error } = await supabase.auth.getUser();

sign-in, sign-out และ token refresh ทั้งหมดเกิดขึ้นแบบ asynchronous ดังนั้นโค้ดฝั่ง UI ต้องมีวิธีตอบสนองต่อการเปลี่ยนแปลงเหล่านี้ แทนที่จะคอย poll เอง supabase.auth.onAuthStateChange ลงทะเบียน listener ที่ยิงทุกครั้งที่ event พวกนี้เกิดขึ้น พร้อมชื่อ event และ session ปัจจุบัน เป็นวิธีมาตรฐานในการทำให้ state ฝั่ง client (nav bar ที่โชว์ว่า sign-in แล้ว, redirect หลัง logout และอื่น ๆ) ตรงกับ auth state จริง

const { data: authListener } = supabase.auth.onAuthStateChange((event, session) => {
console.log(event, session?.user?.id);
// event is one of: SIGNED_IN, SIGNED_OUT, TOKEN_REFRESHED, USER_UPDATED, ...
});
// Later, when the listener is no longer needed:
authListener.subscription.unsubscribe();
flowchart LR
  signin["User signs in"] --> issue["Auth issues access token (JWT) + refresh token"]
  issue --> attach["supabase-js attaches JWT to every API request"]
  attach --> pg[("Postgres / PostgREST reads JWT claims")]
  pg --> uid["auth.uid() available inside RLS policies"]
From sign-in to a JWT Postgres can evaluate
JWT ที่ Supabase Auth ออกให้ ทำให้ row level security policy เข้าถึงอะไรได้บ้าง
onAuthStateChange ใช้ทำอะไร
ความต่างหลักระหว่าง getSession() กับ getUser() คืออะไร
หลัง sign-in supabase-js จัดการ token สองแบบไหนให้เราโดยอัตโนมัติ