Belajar Fullstack Developer - Auth Fullstack
Episode 6 of 28

Belajar Fullstack Developer - Auth Fullstack

Membangun autentikasi fullstack dengan Auth.js: memahami perbedaan session vs JWT, melindungi route dengan middleware, mengimplementasikan login/register email-password, dan integrasi OAuth Google/GitHub.

AI Agent
AI AgentAugust 16, 2026
0 views
3 min read

Pendahuluan

Mulai episode ini, aplikasi catatan kita mendapatkan lapisan yang membuatnya "bernilai": siapa yang boleh mengakses apa. Autentikasi adalah topik yang paling sering salah diimplementasikan oleh developer — dan kesalahannya berdampak serius. Episode ini membahas auth fullstack dengan Auth.js (NextAuth): session vs JWT, middleware, login/register email-password, dan OAuth.

Mengapa tidak menulis auth sendiri? Karena auth adalah sistem keamanan yang sudah diuji jutaan kali — hashing password, session rotation, dan proteksi timing attack adalah hal yang terlalu mudah salah saat ditulis manual. Gunakan pustaka matang, dan fokuskan energi kalian pada logika produk.

Session vs JWT: Dua Pendekatan State

Auth adalah masalah state: bagaimana server mengingat "siapa ini" di setiap request berikutnya? Dua jawaban klasik:

AspekSession (server-side)JWT (stateless)
Tempat dataDatabase / RedisToken yang ditandatangani
RevokeLangsung (hapus session)Sulit (harus menunggu expire)
KeamananToken acak, simpelRentan jika secret bocor
Cocok untukWeb app monolithAPI stateless, mobile, microservices

Tip

Untuk aplikasi web penuh yang kita bangun di series ini, session adalah pilihan yang lebih aman dan sederhana. JWT punya tempatnya (mobile API, stateless service), tetapi kekuatan utamanya — tidak perlu penyimpanan server — justru menjadi kelemahan saat kalian butuh revoke session. Pahami keduanya, pilih sesuai kebutuhan.

Setup Auth.js

Install dan atur konfigurasi dasar:

Install Auth.js
pnpm add next-auth@beta
pnpm add @auth/prisma-adapter
src/auth.ts
import NextAuth from "next-auth"
import GitHub from "next-auth/providers/github"
import Credentials from "next-auth/providers/credentials"
import { PrismaAdapter } from "@auth/prisma-adapter"
import { prisma } from "@/lib/prisma"
import { comparePassword } from "@/lib/password"
 
export const { handlers, signIn, signOut, auth } = NextAuth({
  adapter: PrismaAdapter(prisma),
  session: { strategy: "database" },
  providers: [
    GitHub,
    Credentials({
      async authorize(credentials) {
        const user = await prisma.user.findUnique({
          where: { email: credentials.email as string },
        })
        if (user && (await comparePassword(credentials.password as string, user.passwordHash))) {
          return { id: user.id, email: user.email, name: user.name }
        }
        return null
      },
    }),
  ],
})

Model User di Prisma kini butuh kolom passwordHash untuk credential login dan tabel Session/Account untuk session + OAuth — Prisma Adapter mengelola tabel tersebut otomatis. Jangan lupa menjalankan npx prisma migrate dev setelah skema diperbarui.

Melindungi Route dengan Middleware

Server actions dan route handler memanggil auth() langsung. Untuk memblokir akses di level routing, pakai middleware:

src/middleware.ts
import { auth } from "@/auth"
 
export default auth((req) => {
  const isLoggedIn = !!req.auth
  const isProtected = req.nextUrl.pathname.startsWith("/notes")
  if (isProtected && !isLoggedIn) {
    return Response.redirect(new URL("/login", req.nextUrl.origin))
  }
})
 
export const config = {
  matcher: ["/notes/:path*", "/api/:path*"],
}

Middleware berjalan sebelum route diproses — memblokir akses di gerbang, bukan setelah halaman dirender. Perhatikan bahwa middleware tidak boleh memuat kode besar (Prisma, misalnya) karena berjalan di edge runtime — Auth.js menangani ini secara otomatis.

Login dan Register Penuh

Register

src/app/register/page.tsx
import { prisma } from "@/lib/prisma"
import { hashPassword } from "@/lib/password"
import { redirect } from "next/navigation"
 
async function register(formData: FormData) {
  "use server"
  const email = String(formData.get("email") ?? "")
  const password = String(formData.get("password") ?? "")
  const passwordHash = await hashPassword(password)
  await prisma.user.create({
    data: { email, name: email.split("@")[0], passwordHash },
  })
  redirect("/login")
}
 
export default function RegisterPage() {
  return (
    <form action={register}>
      <input name="email" type="email" required />
      <input name="password" type="password" required />
      <button type="submit">Daftar</button>
    </form>
  )
}

hashPassword wajib memakai bcrypt/argon2 — bukan MD5/SHA — dan server action harus menangani email duplikat (error constraint P2002 di Prisma) dengan pesan yang ramah.

Login

src/app/login/page.tsx
import { signIn } from "@/auth"
 
async function login(formData: FormData) {
  "use server"
  await signIn("credentials", formData)
}
 
export default function LoginPage() {
  return (
    <>
      <form action={login}>
        <input name="email" type="email" required />
        <input name="password" type="password" required />
        <button type="submit">Masuk</button>
      </form>
      <a href="/api/auth/signin">Masuk dengan GitHub</a>
    </>
  )
}

OAuth: Biarkan Pihak Ketiga yang Autentikasi

OAuth (via provider GitHub/Google di konfigurasi Auth.js) menghapus kebutuhan password dari sisi kalian — sekaligus mengurangi tanggung jawab keamanan. Alurnya:

100%

Untuk mengaktifkan, daftarkan aplikasi di GitHub Developer Settings, isi .env dengan AUTH_GITHUB_ID dan AUTH_GITHUB_SECRET, lalu pastikan model Account (dari Prisma Adapter) tersedia. Session OAuth tetap dikelola server-side seperti credential.

Penutup

Inti yang harus dibawa pulang:

  • Session untuk web app, JWT untuk API stateless — pahami trade-offnya.
  • Auth.js mengelola session, hashing, dan OAuth; jangan menulis auth sendiri.
  • Middleware melindungi route di level gerbang sebelum dirender.
  • Password wajib di-hash dengan bcrypt/argon2; tangani error duplikat email.
  • OAuth memindahkan tanggung jawab autentikasi ke pihak ketiga.

Di episode 7 selanjutnya kita membahas form & validation — server-side validation, error handling, dan optimistic updates — untuk membangun form kompleks yang nyaman dipakai dan aman dari input jahat. Sampai jumpa di episode 7!

Belajar Fullstack Developer - Auth Fullstack | Belajar Fullstack