Kembali ke Blog
Tutorial#supabase#authentication#nextjs#security#2026

Supabase Auth + Next.js 16: Panduan Lengkap Authentication 2026

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

Muhamad Putra Aulia Hidayat

19 Maret 20264 menit baca

Supabase Auth + Next.js 16: Authentication yang Aman dan Mudah

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.

Yang Bisa Dilakukan Supabase Auth

  • Email + Password
  • Magic Link (passwordless)
  • OAuth: Google, GitHub, Discord, Twitter, dll
  • Phone/SMS OTP
  • Multi-Factor Authentication (MFA)
  • Row Level Security (data otomatis terisolasi per user)

Setup

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 untuk Protected Routes

// 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).*)"],
}

Login Page

// 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>
  )
}

Row Level Security

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.

Ambil Data User di Server Component

// 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.

supabaseauthenticationnextjssecurity2026

Newsletter Digital Uptime

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.

Tidak ada spam. Unsubscribe kapan saja.

Artikel Terkait

Kami menggunakan cookies untuk meningkatkan pengalaman Anda di website ini. Dengan melanjutkan, Anda menyetujui penggunaan cookies sesuai Kebijakan Privasi kami.