Auth in the Client
ไอเดียในหนึ่งประโยค
หัวข้อที่มีชื่อว่า “ไอเดียในหนึ่งประโยค”supabase.auth ให้ signUp, signInWithPassword และ signOut มาขับเคลื่อน session ให้ onAuthStateChange มา sync UI ของแอปกับ session นั้นให้อัตโนมัติ และมีกฎที่ต้องจำไว้ตลอด — การเช็ก session ในโค้ดฝั่ง client เป็นแค่ UX convenience ส่วน row level security บน database คือ security boundary จริง
flow sign-up, sign-in, sign-out แบบสมบูรณ์
หัวข้อที่มีชื่อว่า “flow sign-up, sign-in, sign-out แบบสมบูรณ์”ทุก call เหล่านี้ resolve เป็น { data, error } ไม่ throw exception ดังนั้นต้องเช็ก error ก่อนเชื่อผลลัพธ์ — pattern เดียวกับที่จะเห็นแบบเป็นทางการในบทถัดไป
// Sign up a new userconst { data: signUpData, error: signUpError } = await supabase.auth.signUp({ password: 'correct-horse-battery-staple',});
if (signUpError) { console.error('sign up failed:', signUpError.message);}
// Sign in an existing userconst { data: signInData, error: signInError } = await supabase.auth.signInWithPassword({ password: 'correct-horse-battery-staple',});
if (signInError) { console.error('sign in failed:', signInError.message);}
// Sign out the current userconst { error: signOutError } = await supabase.auth.signOut();
if (signOutError) { console.error('sign out failed:', signOutError.message);}signUp หรือ signInWithPassword ที่สำเร็จจะเก็บ session (access token กับ refresh token) ไว้ใน client และ request ถัดไปทุกอันจาก client นั้นจะถูกส่งในฐานะ user ที่ authenticated แล้ว
Sync UI state ด้วย onAuthStateChange
หัวข้อที่มีชื่อว่า “Sync UI state ด้วย onAuthStateChange”แทนที่จะไล่เช็กเองว่า user sign in อยู่หรือไม่ — แล้วต้องมาเช็กซ้ำทุกครั้งหลัง sign-in, sign-out หรือ token refresh แบบเงียบ ๆ — subscribe แค่ครั้งเดียวตอนแอป start ก็พอ
supabase.auth.onAuthStateChange((event, session) => { // Fires on SIGNED_IN, SIGNED_OUT, TOKEN_REFRESHED, and more setSession(session);});subscription เดียวนี้ fire ทุก event ที่เกี่ยวกับ auth ตลอดอายุแอป ทั้ง session แรกตอนโหลด, sign-in, sign-out และ token refresh ที่เกิดขึ้นเงียบ ๆ อยู่เบื้องหลัง การผูก local state ของแอปเข้ากับ callback ตัวเดียวนี้แปลว่า UI จะสะท้อน session จริงของ client อยู่เสมอ และไม่ต้องมานั่งจำว่าต้องเรียกอะไรเองหลัง auth action แต่ละครั้ง
Protect route: UX convenience ไม่ใช่ security จริง
หัวข้อที่มีชื่อว่า “Protect route: UX convenience ไม่ใช่ security จริง”pattern ที่พบบ่อยฝั่ง client คือเช็ก session ก่อน render หน้าที่ต้อง protect หรือก่อนยิง request ที่ต้อง authenticated
const { data: { session },} = await supabase.auth.getSession();
if (!session) { // No session: hide the page and send the user to sign in redirectToLogin();}สิ่งนี้มีประโยชน์จริง เพราะช่วยไม่ให้ protected content แวบขึ้นมาให้คนที่ sign out อยู่เห็น และไม่ต้องยิง request ที่รู้อยู่แล้วว่าจะ fail แต่ห้ามเข้าใจผิดว่านี่คือกลไก security จริง โค้ด JavaScript ฝั่ง client ถูก inspect, แก้ไข หรือข้ามไปเลยได้เสมอโดยคนที่ตั้งใจจะทำ ไม่มีอะไรห้ามใครสักคนยิงตรงไปที่ Supabase project ด้วย token ของตัวเอง ข้าม UI ของคุณไปทั้งหมด สิ่งที่เป็น boundary จริงว่า request หนึ่งจะอ่านหรือเขียน row ไหนได้บ้างคือ row level security (RLS) ที่ Postgres บังคับใช้เองฝั่ง server ไม่ว่า client จะเช็กหรือ render อะไรก็ตาม
flowchart LR
action["signUp() or signInWithPassword()"] --> session["Session created and stored in the client"]
session --> event["onAuthStateChange fires"]
event --> ui["App UI updates; a protected route becomes reachable"]
ui --> rls[("Real enforcement still happens via RLS on the database")]