Objective
Your mission
Implement signup, login, session check, auth state listener, logout, and a protected route — then audit for common auth mistakes. Output: auth_setup.md.
You need: A Supabase-connected app (from Lesson 02).
Time: 35 minutes.
Step 1: learn the auth flow (5 min)
The complete auth flow has 4 parts:
- Signup: User creates account → Supabase creates user + returns session
- Login: User enters credentials → Supabase verifies + returns session
- Session: Token persists across refreshes → app checks session to know if logged in
- Logout: User signs out → session destroyed → redirect to login
Client-side vs. server-side protection
Auth has two layers: (1) Client-side — frontend checks if user is logged in, redirects to login if not. This is UX. (2) Server-side — RLS policies on the database restrict data access. This is security. You need BOTH. Client-side only: a user can bypass the frontend and query the database directly. Server-side only: users see pages they shouldn't before being redirected.
Step 2: implement signup and login (10 min)
Ask the AI to implement auth forms, or write them yourself:
// Signup
const { data, error } = await supabase.auth.signUp({
email: 'user@example.com',
password: 'securepassword'
})
if (error) {
showError(error.message)
} else {
// User created — check email for confirmation (if enabled)
}
// Login
const { data, error } = await supabase.auth.signInWithPassword({
email: 'user@example.com',
password: 'securepassword'
})
if (error) {
showError('Invalid email or password')
} else {
navigate('/dashboard')
}
Write in your file:
## Auth Forms
Signup form implemented: [yes/no]
Login form implemented: [yes/no]
Error handling on both: [yes/no]
Step 3: add session check and auth state listener (5 min)
// Check session on app load
const { data: { session } } = await supabase.auth.getSession()
if (session) {
setUser(session.user)
} else {
navigate('/login')
}
// Listen for auth changes
supabase.auth.onAuthStateChange((event, session) => {
if (event === 'SIGNED_OUT') {
navigate('/login')
} else if (event === 'SIGNED_IN') {
setUser(session.user)
}
})
// Logout
await supabase.auth.signOut()
Write in your file:
## Session Management
Session check on app load: [yes/no]
Auth state change listener: [yes/no]
Logout button: [yes/no]
Step 4: create a protected route (5 min)
function ProtectedRoute({ children }) {
const [user, setUser] = useState(null)
const [loading, setLoading] = useState(true)
useEffect(() => {
supabase.auth.getSession().then(({ data: { session } }) => {
if (session) setUser(session.user)
setLoading(false)
})
}, [])
if (loading) return <LoadingSpinner />
if (!user) return <Navigate to="/login" />
return children
}
Write in your file:
## Protected Route
Route protected: [e.g., /dashboard]
Auth check is NOT commented out: [yes/no]
Redirects to /login when not authenticated: [yes/no]
Step 5: test the full flow (5 min)
Test: signup → login → see dashboard → logout → redirected to login.
Write in your file:
## Flow Test
1. Signup: [success/fail]
2. Login: [success/fail]
3. Dashboard visible after login: [yes/no]
4. Logout works: [yes/no]
5. Redirected to login after logout: [yes/no]
Step 6: audit for 5 common auth mistakes (5 min)
Check your AI-generated code for these common mistakes:
- Commented-out auth check —
// if (!user) return <Navigate to="/login" /> — uncomment it - Client-side only protection — no RLS on database — enable RLS
- Hardcoded test credentials — remove them, use a signup form
- No session check on page load — add
supabase.auth.getSession() on app load - No error handling on auth — add
if (error) checks with user-facing messages
Write in your file:
## Auth Mistake Audit
1. Commented-out auth check: [found/fixed/not present]
2. Client-side only (no RLS): [found/fixed/not present]
3. Hardcoded credentials: [found/fixed/not present]
4. No session check on load: [found/fixed/not present]
5. No error handling on auth: [found/fixed/not present]
RLS enabled on database tables: [yes/no]
Both client-side AND server-side protection in place: [yes/no]
Your app redirects logged-out users away from /dashboard, but the habits table has no RLS policy. Is the data protected?
Client-side checks are UX; the database's RLS policies are the security. Without RLS, someone can skip the frontend and query the table directly.