Toko Kue: Proteksi Halaman & Info User (Supabase Auth)
Jaga halaman pentingmu hanya untuk member! Pelajari cara melindungi rute di Next.js agar hanya bisa diakses oleh pengguna yang sudah login, dan cara menampilkan informasi pengguna (seperti email) menggunakan Supabase Auth.
17m bacaanKuis WajibMicro Badge Lesson
"Area Khusus Member" di Toko Kue: Lindungi Halaman & Sapa Pengguna!
Toko Kue kita udah punya pintu masuk (Login & Registrasi) pake Supabase Auth! Keren! Sekarang, gimana caranya kita bikin beberapa "ruangan" di toko kita jadi eksklusif cuma buat pengguna yang udah login? Misalnya, halaman Checkout, atau nanti kalau ada halaman Profil Pengguna.
Selain itu, kita juga pengen dong bisa nyapa pengguna yang udah login dengan namanya (atau emailnya) di header atau di halaman tertentu.
Di bagian ini, kita bakal belajar cara:
Ngelindungin halaman (rute) di Next.js biar cuma bisa diakses sama pengguna yang udah login.
Nampilin informasi pengguna yang lagi login.
Kita bakal banyak manfaatin status sesi dari Supabase Auth yang udah kita singgung di komponen Navbar sebelumnya.
Mengelola Status Autentikasi Secara Global (Review CartContext atau Buat AuthContext)
Cara paling enak buat tau status login pengguna di banyak komponen adalah dengan nyimpen informasi sesi atau pengguna di React Context. Di studi kasus Toko Kue, kita udah punya yang di-provide di .
cart-context.tsx
RootLayout
Kita bisa memperluas CartContext untuk sekalian nyimpen info user, atau bikin AuthContext baru khusus buat urusan autentikasi. Untuk menjaga modularitas, membuat AuthContext baru seringkali lebih baik, tapi untuk contoh ini, mari kita bayangkan kita memodifikasi CartContext (atau RootLayout jika state user ada di sana) untuk juga menyediakan info user.
Modifikasi Konseptual di cart-context.tsx atau RootLayout (jika state user ada di sana):
(Ingat, di materi sebelumnya, kita sudah membuat Navbar.tsx yang mengambil state user. Logika serupa bisa dipusatkan).
tsx
// Contoh konseptual jika state user dikelola di CartProvider atau RootLayout// File: src/context/cart-context.tsx// ... import lainnya ...import { User, Session } from '@supabase/supabase-js';// Tambahkan user dan session ke CartContextTypeinterface CartContextType { // ... (props keranjang yang sudah ada) ... user: User | null; session: Session | null; isLoadingAuth: boolean; // Buat nandain kalau status auth lagi dicek signOut: () => Promise<void>;}// Di dalam CartProvider (atau RootLayout)export function CartProvider({ children }: CartProviderProps) { // ... (state keranjang yang sudah ada) ... const [user, setUser] = useState<User | null>(null); const [session, setSession] = useState<Session | null>(null); const [isLoadingAuth, setIsLoadingAuth] = useState(true); // Awalnya true const router = useRouter(); // Jika mau redirect dari sini useEffect(() => { async function getSessionData() { const { data: { session: currentSession }, error } = await supabase.auth.getSession(); if (error) { console.error("Error getting session:", error); } setSession(currentSession); setUser(currentSession?.user ?? null); setIsLoadingAuth(false); // Selesai loading status auth awal } getSessionData(); const { data: authListener } = supabase.auth.onAuthStateChange((_event, newSession) => { console.log("Auth state changed:", _event, newSession); setSession(newSession); setUser(newSession?.user ?? null); setIsLoadingAuth(false); // Selesai loading juga pas ada perubahan // Contoh: kalau event-nya SIGNED_OUT dan kita lagi di halaman terproteksi, redirect // if (_event === 'SIGNED_OUT' && window.location.pathname.startsWith('/checkout')) { // router.push('/auth/signin'); // } }); return () => { authListener?.subscription?.unsubscribe(); }; }, [router]); // Tambahkan router jika digunakan di effect const signOut = async () => { await supabase.auth.signOut(); // setUser(null); setSession(null); // akan dihandle oleh onAuthStateChange router.push('/auth/signin'); // Arahkan setelah logout }; const contextValue = { // ... (value keranjang yang sudah ada) ... user, session, isLoadingAuth, signOut }; return ( <CartContext.Provider value={contextValue}> {children} </CartContext.Provider> );}
Kita mau halaman /checkout cuma bisa diakses kalau pengguna udah login. Kalau belum, kita arahin dia ke halaman login.
Modifikasi src/app/checkout/page.tsx:
tsx
// src/app/checkout/page.tsx"use client";import React, { useState, FormEvent, useEffect } from 'react'; // Tambah useEffectimport Link from 'next/link';import { useRouter } from 'next/navigation';import { useCart } from '@/context/cart-context'; // Impor useCartexport default function HalamanCheckout() { const router = useRouter(); // Ambil user, session, dan isLoadingAuth dari context
Bedah Perubahan di HalamanCheckout.tsx:
useCart(): Kita sekarang ngambil user, session, dan isLoadingAuth dari CartContext kita (atau AuthContext kalau kamu bikin terpisah).
useEffect buat Cek Login:
Efek ini jalan pas user, session, atau isLoadingAuth berubah.
Kalau isLoadingAuth udah false (artinya pengecekan sesi awal udah selesai) DAN session-nya gak ada (artinya gak login), kita router.push('/auth/signin?redirect=/checkout').
Parameter ?redirect=/checkout itu trik biar setelah login berhasil, kita bisa arahin pengguna balik lagi ke halaman checkout.
UI Loading & Not Logged In: Kita nambahin tampilan sementara pas isLoadingAuth masih true, dan pesan kalau pengguna belum login (meskipun idealnya udah ke-redirect).
Di handleSubmitCheckout, kita juga bisa nambahin pengecekan if (!user) sebelum proses.
Kita juga nampilin email pengguna yang lagi login.
Langkah 2: Menampilkan Info Pengguna (Contoh di Navbar.tsx)
Kita udah pernah bikin Navbar.tsx yang nampilin status login. Sekarang kita pastikan dia pake state user dari context yang udah kita siapkan.
// src/app/components/navbar.tsx"use client";import React from 'react'; // useEffect dan useState tidak lagi dikelola di sini jika user state dari contextimport Link from 'next/link';import { useCart } from '@/context/cart-context'; // Pake useCart yang sekarang punya info user & signOut// import { supabase } from '@/lib/supabase-client'; // Tidak perlu import supabase langsung di sini lagi// import { User } from '@supabase/supabase-js'; // Tipe User bisa diimpor dari context atau @supabase/supabase-jsimport { useRouter } from 'next/navigation';export default function Navbar() { const { user, signOut, totalItem, isLoadingAuth } = useCart(); // Ambil user dan signOut dari context const router = useRouter(); // router tetap dibutuhkan untuk redirect setelah signOut // Logika useEffect buat getSession dan onAuthStateChange sekarang ada di CartProvider/AuthProvider const handleSignOut = async () => { await signOut(); // Panggil fungsi signOut dari context // Redirect udah dihandle di fungsi signOut di context atau bisa juga di sini }; return ( <nav className="bg-gray-800 p-4 text-white"> <div className="container mx-auto flex justify-between items-center"> <Link href="/" className="font-bold text-xl hover:text-gray-300"> Toko Kue Mahir.dev </Link> <div className="flex items-center space-x-4"> <Link href="/keranjang" className="hover:text-gray-300"> Keranjang ({isLoadingAuth ? '...' : totalItem}) </Link> {isLoadingAuth ? ( <span>Loading...</span> ) : user ? ( <> <span className="hidden sm:inline">Halo, {user.email}</span> <button onClick={handleSignOut} className="bg-red-500 hover:bg-red-600 px-3 py-1 rounded text-sm" > Logout </button> </> ) : ( <> <Link href="/auth/signin" className="hover:text-gray-300">Masuk</Link> <Link href="/auth/signup" className="bg-green-500 hover:bg-green-600 px-3 py-1 rounded text-sm"> Daftar </Link> </> )} </div> </div> </nav> );}
Perubahan di Navbar.tsx:
Sekarang dia ngambil user, signOut, totalItem, dan isLoadingAuth dari useCart().
Logika useEffect buat getSession dan onAuthStateChange udah dipindahin ke CartProvider (atau AuthProvider jika ada). Navbar jadi lebih "bersih" dan cuma fokus nampilin UI berdasarkan state dari context.
handleSignOut sekarang manggil fungsi signOut dari context.
Alternatif Proteksi Rute: Middleware Next.js (Lebih Advance)
Cara pake useEffect buat redirect di Client Component itu lumayan oke buat kasus simpel. Tapi, buat proteksi rute yang lebih robust dan aman (terutama biar konten halaman terproteksi gak sempet ke-render sama sekali di klien kalau belum login), Next.js nyediain fitur Middleware.
Middleware itu kode yang jalan di server sebelum request nyampe ke halaman. Kamu bisa pake middleware buat ngecek sesi pengguna (misal, dari cookie Supabase), terus nge-redirect ke halaman login kalau sesi gak valid.
Ini topik yang lebih advance, tapi bagus buat diketahui. Kamu bisa bikin file middleware.ts (atau .js) di root proyekmu (sejajar src/ atau app/).
Ini di luar cakupan panduan dasar kita, tapi sebagai gambaran, kamu bisa ngelakuin ini.
Dengan adanya proteksi halaman dan tampilan info pengguna, aplikasi Toko Kue kita jadi kerasa lebih personal dan aman. Pengguna yang belum login gak bisa "nyelonong" ke halaman checkout, dan pengguna yang udah login bisa disapa dengan baik.
Ini adalah langkah penting dalam ngebangun aplikasi web yang punya manajemen pengguna. Kamu bisa ngembangin lagi, misalnya bikin halaman profil pengguna di mana mereka bisa ngubah data mereka sendiri.
Checkpoint KuisTarget: 70%
Kuis Proteksi Halaman & Info User (Supabase Auth)
Kuis ini wajib untuk sertifikat. Kamu tetap bisa lanjut ke pelajaran berikutnya dan mengerjakannya nanti.