Skip to content

Authentication

Auth Flow Overview

Sistem auth menggunakan better-auth dengan Drizzle adapter, running di Cloudflare Workers. Ada 3 metode login dan 4 role yang saling eksklusif.

Stack

  • better-auth 1.6.9 (latest stable)
  • Drizzle adapter with SQLite provider
  • Cloudflare Workers runtime

Login Methods

1. Google OAuth

User klik "Login dengan Google" → redirect ke Google consent → callback ke /api/auth/callback/google → session cookie di-set → redirect ke app.

Setup:

  • Google Client ID dan Client Secret disimpan sebagai Cloudflare secrets.
  • Redirect URI harus didaftarkan di Google Console: https://<domain>/api/auth/callback/google.
  • BETTER_AUTH_URL harus di-set ke domain aplikasi (e.g., https://xprivate.id) untuk menghindari redirect_uri_mismatch error.

Frontend:

ts
// apps/web/src/lib/auth.ts
import { createAuthClient } from "better-auth/svelte";

export const authClient = createAuthClient({
  baseURL: import.meta.env.VITE_API_URL || undefined,
});
svelte
<script>
  import { authClient } from "$lib/auth";
  const session = authClient.useSession();
</script>

<button onclick={() => authClient.signIn.social({ provider: "google" })}>
  Masuk dengan Google
</button>

2. Email / Password

User input email + password → /api/auth/sign-in/email → better-auth verify dengan scrypt → session cookie di-set.

Security:

  • Password di-hash dengan scrypt via node:crypto.scrypt.
  • nodejs_compat flag diaktifkan di wrangler config.
  • Tidak pakai bcrypt atau argon2 (native bindings tidak work di Workers).

User input email → klik "Kirim Magic Link" → Resend kirim email dengan link → user klik link → auto-login → session cookie di-set.

Setup:

  • Resend API key disimpan sebagai Cloudflare secret.
  • Email template dikelola di @packages/email.

Role System

4 Roles

RoleDeskripsiDibuat oleh
studentStudent — bisa create order, confirm sessionsSelf-registration
teacherTeacher — bisa confirm sessions, submit presenceSelf-registration (perlu admin verification)
adminBackoffice staff — full admin access kecuali inviteOwner invitation only
ownerPlatform owner — full access + bisa invite adminsManual seed/setup

Role Rules

  • Mutual exclusion: admin dan owner tidak bisa jadi student atau teacher. Sebaliknya, student dan teacher tidak bisa jadi admin atau owner.
  • Owner invite: Hanya owner yang bisa invite admin baru via endpoint POST /admin/v1/inviteAdmin.
  • Default role: Registrasi baru default ke student. Teacher yang register mungkin perlu approval admin (configurable).

Middleware Chain

oRPC menggunakan middleware chain untuk progressive context enrichment:

ProcedureMiddleware ChainUse For
publicProcedurebasePublic routes (health, landing data)
authedProcedurebase.use(withPassiveAuth).use(requireAuth)Any logged-in user
studentProcedureauthedProcedure.use(requireStudent)Role = student
teacherProcedureauthedProcedure.use(requireTeacher)Role = teacher
adminProcedureauthedProcedure.use(requireAdminOrOwner)Role = admin atau owner
ownerProcedureauthedProcedure.use(requireOwner)Role = owner only

Session Strategy

  • better-auth menggunakan database sessions (D1-backed).
  • Session cookie name: glp.session_token
  • Session middleware resolve passively dulu, then require auth untuk protected routes.

Bearer Token (Non-Browser / API Testing)

Untuk non-browser clients dan OpenAPI testing, better-auth Bearer plugin mengizinkan autentikasi via Authorization: Bearer <token> header. Bearer token di-obtain dari response header set-auth-token setelah sign-in.

JWT Plugin

JWT plugin menyediakan:

  • Endpoint /api/auth/token — generate JWT token dari session aktif
  • Endpoint /api/auth/jwks — public key set untuk verifikasi JWT eksternal
  • Table jwks di D1 untuk menyimpan key pair

JWT tidak menggantikan database session — digunakan untuk services yang memerlukan JWT atau testing dari OpenAPI client.

Flow:

  1. Sign-in via Google/Email/Magic Link → dapat session cookie
  2. Panggil GET /api/auth/token dengan session cookie → dapat JWT
  3. Gunakan Authorization: Bearer <jwt> untuk API calls selanjutnya

2FA (Post-MVP)

  • better-auth support 2FA via TOTP plugin.
  • Jangan implementasikan di MVP. Schema harus allow future addition tapi tidak ada UI atau API untuk itu dulu.

Env Variables

VariablePurpose
BETTER_AUTH_URLBase URL aplikasi (e.g., https://xprivate.id)
BETTER_AUTH_SECRETSecret untuk session signing
GOOGLE_CLIENT_IDGoogle OAuth client ID
GOOGLE_CLIENT_SECRETGoogle OAuth client secret
RESEND_API_KEYResend API key untuk email
RESEND_FROM_EMAILFrom address untuk email (e.g. noreply@xprivate.id)

Decisions

Scrypt over bcrypt/argon2

bcrypt dan argon2 memerlukan native bindings yang tidak work di Cloudflare Workers. node:crypto.scrypt dengan nodejs_compat flag sudah verified working dan secure.

Database sessions sebagai default

Database sessions lebih aman untuk platform ini karena:

  • Session bisa di-revoke secara instan (penting untuk admin actions).
  • Tidak perlu handle JWT refresh complexity.
  • D1 adalah edge database — latency session lookup minimal.

JWT hanya digunakan untuk specific use cases: non-browser clients, external service integration, dan OpenAPI testing.

Released under the MIT License.