Kami menggunakan cookies untuk meningkatkan pengalaman Anda di website ini. Dengan melanjutkan, Anda menyetujui penggunaan cookies sesuai Kebijakan Privasi kami.
Authentication adalah bagian tersulit dalam web development. Dengan Supabase Auth dan Next.js 16, proses ini jadi jauh lebih mudah — tanpa korbankan keamanan. Ini implementasi lengkapnya.
Muhamad Putra Aulia Hidayat
Membangun auth dari scratch itu ribet dan berisiko. Supabase Auth memberikan solusi yang production-ready, secure, dan tetap fleksibel. Ini cara implementasinya di Next.js 16.
npm install @supabase/supabase-js @supabase/ssr
// lib/supabase/client.ts - Browser client
import { createBrowserClient } from "@supabase/ssr"
export function createClient() {
return createBrowserClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
)
}
// lib/supabase/server.ts - Server client
import { createServerClient } from "@supabase/ssr"
import { cookies } from "next/headers"
export async function createClient() {
const cookieStore = await cookies()
return createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
getAll: () => cookieStore.getAll(),
setAll: (cookiesToSet) => {
cookiesToSet.forEach(({ name, value, options }) =>
cookieStore.set(name, value, options)
)
}
}
}
)
}
// middleware.ts
import { createServerClient } from "@supabase/ssr"
import { NextResponse } from "next/server"
import type { NextRequest } from "next/server"
export async function middleware(request: NextRequest) {
let supabaseResponse = NextResponse.next({ request })
const supabase = createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
getAll: () => request.cookies.getAll(),
setAll: (cookiesToSet) => {
cookiesToSet.forEach(({ name, value, options }) => {
supabaseResponse.cookies.set(name, value, options)
})
}
}
}
)
const { data: { user } } = await supabase.auth.getUser()
const isAuthPage = request.nextUrl.pathname.startsWith("/login") ||
request.nextUrl.pathname.startsWith("/register")
const isProtected = request.nextUrl.pathname.startsWith("/dashboard")
if (!user && isProtected) {
return NextResponse.redirect(new URL("/login", request.url))
}
if (user && isAuthPage) {
return NextResponse.redirect(new URL("/dashboard", request.url))
}
return supabaseResponse
}
export const config = {
matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"],
}
// app/login/page.tsx
"use client"
import { useState } from "react"
import { createClient } from "@/lib/supabase/client"
import { useRouter } from "next/navigation"
export default function LoginPage() {
const [email, setEmail] = useState("")
const [password, setPassword] = useState("")
const [loading, setLoading] = useState(false)
const [error, setError] = useState<string | null>(null)
const router = useRouter()
const supabase = createClient()
const handleLogin = async (e: React.FormEvent) => {
e.preventDefault()
setLoading(true)
setError(null)
const { error } = await supabase.auth.signInWithPassword({ email, password })
if (error) {
setError(error.message)
setLoading(false)
return
}
router.push("/dashboard")
router.refresh()
}
const handleGoogleLogin = async () => {
await supabase.auth.signInWithOAuth({
provider: "google",
options: { redirectTo: `${location.origin}/auth/callback` }
})
}
return (
<form onSubmit={handleLogin}>
<input type="email" value={email} onChange={e => setEmail(e.target.value)} />
<input type="password" value={password} onChange={e => setPassword(e.target.value)} />
{error && <p className="text-red-500">{error}</p>}
<button type="submit" disabled={loading}>Masuk</button>
<button type="button" onClick={handleGoogleLogin}>Login dengan Google</button>
</form>
)
}
Ini yang membuat Supabase sangat aman — data user secara otomatis terisolasi:
-- Aktifkan RLS
ALTER TABLE notes ENABLE ROW LEVEL SECURITY;
-- User hanya bisa lihat data miliknya
CREATE POLICY "Users see own notes"
ON notes FOR SELECT
USING (user_id = auth.uid());
-- User hanya bisa insert data miliknya
CREATE POLICY "Users insert own notes"
ON notes FOR INSERT
WITH CHECK (user_id = auth.uid());
-- User hanya bisa update data miliknya
CREATE POLICY "Users update own notes"
ON notes FOR UPDATE
USING (user_id = auth.uid());
Dengan RLS aktif, bahkan kalau ada bug di kode API Anda, user tidak bisa mengakses data orang lain.
// app/dashboard/page.tsx
import { createClient } from "@/lib/supabase/server"
import { redirect } from "next/navigation"
export default async function Dashboard() {
const supabase = await createClient()
const { data: { user } } = await supabase.auth.getUser()
if (!user) redirect("/login")
const { data: notes } = await supabase
.from("notes")
.select("*")
.order("created_at", { ascending: false })
// RLS otomatis filter hanya notes milik user ini
return (
<div>
<h1>Halo, {user.email}</h1>
{notes?.map(note => <p key={note.id}>{note.content}</p>)}
</div>
)
}
Dengan setup ini, Anda punya authentication yang production-ready dalam hitungan jam, bukan hari.
Tips teknologi & bisnis mingguan
Bergabung dengan 2,500+ subscriber yang mendapatkan insight teknologi, tutorial development, dan tips bisnis digital langsung ke inbox mereka setiap minggu.
Dapatkan tips & insight teknologi terbaru langsung ke inbox Anda.
© 2026 PT Digital Uptime Teknologi Informasi. Hak cipta dilindungi.