Lewati ke konten
create-nextjs-kit

auth

Alasan ada: pendaftaran, login, dan sesi yang aman tanpa menulis ulang pengelolaan kata sandi, cookie, dan tabel sesi.

Mode: fullstack. Otomatis memasang db. (Untuk mode frontend dengan backend terpisah, pakai bff-auth.)

Terminal window
npm create nextjs-kit@latest my-app -- --mode fullstack --modules auth
Terminal window
cp .env.example .env.local
npm run db:up && npm run db:migrate
npm run dev

Buka /register untuk membuat akun, lalu /dashboard. /dashboard diproteksi; ubah daftar route terproteksi di src/config/auth.ts.

Bagian Isi
src/server/auth/index.ts Konfigurasi Better Auth (adapter Drizzle, email dan kata sandi)
src/server/auth/session.ts getSession() dan requireUser()
src/server/db/auth-schema.ts Tabel user, session, account, verification
drizzle/0001_auth.sql Migrasi untuk keempat tabel itu
src/app/api/auth/[...all] Endpoint HTTP Better Auth
src/features/auth Form masuk dan daftar, tombol keluar
src/proxy.ts Pagar optimis untuk route terproteksi
e2e/auth.spec.ts E2E: daftar, masuk, keluar, rate limit
import { requireUser } from "@/server/auth/session";
export default async function Page() {
const user = await requireUser();
// ...
}

proxy.ts hanya mengecek keberadaan cookie sesi dan tidak menyentuh database, jadi cookie palsu lolos dari sana. requireUser() yang memvalidasi sesi. Pakai di halaman, Server Action, dan DAL yang butuh login.

Kenapa klien Better Auth, bukan Server Action

Section titled “Kenapa klien Better Auth, bukan Server Action”

Form memanggil authClient (HTTP ke /api/auth/*), tidak memanggil auth.api.signInEmail dari Server Action. Alasannya keamanan: rate limit dan pengecekan origin Better Auth berjalan di lapisan HTTP, sehingga pemanggilan langsung dari server melewatinya dan login jadi bisa di-brute-force. Ini sedikit menyimpang dari pola “mutasi = Server Action” di tempat lain.

Percobaan /sign-in dan /sign-up dibatasi 3 permintaan per 10 detik per IP (aktif di mode production). Agar itu bekerja, Better Auth harus bisa menentukan IP klien yang tepercaya. Kalau tidak, semua pengguna berbagi satu bucket, sehingga siapa pun bisa mengunci login orang lain.

Better Auth membaca x-forwarded-for:

  • Tanpa konfigurasi, header harus berisi tepat satu IP valid. Di nginx, timpa header itu:
    proxy_set_header X-Forwarded-For $remote_addr;
    Jangan pakai $proxy_add_x_forwarded_for tanpa AUTH_TRUSTED_PROXIES, karena header menjadi rantai (klien, proxy) dan IP tidak bisa ditentukan.
  • Atau isi AUTH_TRUSTED_PROXIES dengan IP atau CIDR proxy Anda (dipisah koma). Better Auth lalu membaca rantai dari kanan, melewati proxy tepercaya, dan memakai IP klien yang sebenarnya.

Aplikasi sebaiknya hanya bisa dijangkau lewat proxy itu. Kalau terbuka langsung ke internet, klien bisa memalsukan x-forwarded-for dan melewati rate limit.

Penyimpanan rate limit bawaan ada di memori proses, jadi tidak dibagi antar instance. Untuk beberapa instance, pindahkan ke database atau penyimpanan bersama (lihat dokumentasi Better Auth).

Variabel Default Keterangan
BETTER_AUTH_SECRET Hanya saat dev dan test Minimal 32 karakter. Wajib di production: build dan start gagal bila kosong. Buat dengan openssl rand -base64 32
AUTH_TRUSTED_PROXIES kosong IP atau CIDR reverse proxy, dipisah koma
NEXT_PUBLIC_APP_URL http://localhost:3000 Dipakai sebagai baseURL; harus sama dengan origin yang dibuka pengguna

Nilai bawaan BETTER_AUTH_SECRET untuk dev tidak aman dan sengaja tidak berlaku di production.

  • Migrasi 0001_auth dibuat dengan drizzle-kit di atas migrasi db. Modul ini menimpa drizzle/meta/_journal.json dengan versi yang memuat kedua migrasi.
  • auth-schema.test.ts membandingkan tabel Drizzle dengan definisi dari paket better-auth; kalau paket diperbarui dan menambah field, test gagal sebelum login rusak di runtime.
  • Verifikasi email, reset kata sandi, dan login sosial belum disertakan.
  • Bersama dal, todos menjadi milik per pengguna (lihat bagian itu).
  • CI proyek selalu menyiapkan Postgres dan menjalankan migrasi untuk E2E auth. Script test:db hanya dijalankan bila modul dal juga terpasang.