Sessions and JWTs
ไอเดียในหนึ่งประโยค
หัวข้อที่มีชื่อว่า “ไอเดียในหนึ่งประโยค”sign-in สำเร็จหนึ่งครั้งจะได้ JWT access token อายุสั้นมาพร้อมกับ refresh token อายุยาวกว่า supabase-js จะเก็บและ refresh ทั้งคู่ให้เองแบบเงียบ ๆ และ claim ที่อยู่ใน JWT — user ID กับ role — นี่แหละที่ Postgres ใช้ตัดสินว่า request หนึ่ง ๆ มีสิทธิ์เห็นอะไรบ้าง
สิ่งที่ได้กลับมาหลัง sign-in
หัวข้อที่มีชื่อว่า “สิ่งที่ได้กลับมาหลัง sign-in”ทุกครั้งที่ 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 JWTconsole.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 คนปัจจุบันหรือเปล่า
อ่าน session ปัจจุบัน: getSession vs getUser
หัวข้อที่มีชื่อว่า “อ่าน session ปัจจุบัน: getSession vs getUser”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 neededconst { data: { session } } = await supabase.auth.getSession();
// Authoritative: revalidates the access token against the Auth serverconst { data: { user }, error } = await supabase.auth.getUser();ให้ทุกอย่างซิงก์กัน: onAuthStateChange
หัวข้อที่มีชื่อว่า “ให้ทุกอย่างซิงก์กัน: onAuthStateChange”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"]