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

Auth in the Client

supabase.auth ให้ signUp, signInWithPassword และ signOut มาขับเคลื่อน session ให้ onAuthStateChange มา sync UI ของแอปกับ session นั้นให้อัตโนมัติ และมีกฎที่ต้องจำไว้ตลอด — การเช็ก session ในโค้ดฝั่ง client เป็นแค่ UX convenience ส่วน row level security บน database คือ security boundary จริง

ทุก call เหล่านี้ resolve เป็น { data, error } ไม่ throw exception ดังนั้นต้องเช็ก error ก่อนเชื่อผลลัพธ์ — pattern เดียวกับที่จะเห็นแบบเป็นทางการในบทถัดไป

// Sign up a new user
const { 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 user
const { 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 user
const { 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 แล้ว

แทนที่จะไล่เช็กเองว่า 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 แต่ละครั้ง

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")]
A session drives the UI, but RLS is still the real gate
การ subscribe onAuthStateChange แค่ครั้งเดียวช่วยให้ไม่ต้องทำอะไรเอง
ทำไมการเช็ก session ก่อน render หน้า protected ถึงเป็นแค่ UX convenience
อะไรคือตัวที่บังคับจริงว่า request หนึ่งอ่านหรือเขียน row ไหนได้บ้าง ไม่ว่า client จะเช็กอะไรก็ตาม
call อย่าง supabase.auth.signOut() resolve ออกมาเป็นรูปแบบไหน