Di session-based authentication, server mengingat siapa yang login di catatannya sendiri, namanya session store, lalu cuma memberi browser sebuah nomor acak, namanya session ID, di dalam cookie. Mirip penitipan tas di supermarket: tasmu disimpan di balik meja, kamu cuma pegang kartu bernomor. Kartunya gak berisi apa-apa tentang kamu. Waktu kamu pulang, petugas mengambil kartunya dan mengosongkan raknya.
🧪 Tebakan cepat: Portal Sekolah dijalankan di dua server, dan tiap server menyimpan session di memorinya sendiri. Siti login lewat server A, lalu request berikutnya diarahkan ke server B. Di server B, Siti masih dianggap login atau gak? Tebak dulu, jawabannya ada di bagian "Satu server, dua server".
Alurnya, request demi request
Login, lalu buka rapor, lalu logout. Ini percakapan HTTP-nya di Portal Sekolah, persis yang ditampilkan Lab (session ID-mu di Lab acak, jadi beda):
POST /login HTTP/1.1
Host: portal.sekolah.test
Content-Type: application/json
{"email": "[email protected]", "password": "rahasia123"}
HTTP/1.1 200 OK
Set-Cookie: sid=3f9c2a7e51d04b8c9e16a2f07d3b5c41; Path=/; Max-Age=7200; HttpOnly; Secure; SameSite=Lax
GET /students/1/grades HTTP/1.1
Host: portal.sekolah.test
Cookie: sid=3f9c2a7e51d04b8c9e16a2f07d3b5c41
HTTP/1.1 200 OK
POST /logout HTTP/1.1
Cookie: sid=3f9c2a7e51d04b8c9e16a2f07d3b5c41
HTTP/1.1 204 No Content
Set-Cookie: sid=; Path=/; Max-Age=0
- Password cocok, jadi server membuat catatan baru di session store:
3f9c2a... -> { userId: 1, role: "student" }. - Server mengirim
Set-Cookie, dan browser menyimpannya. Mulai sekarang, browser menempelkanCookie: sid=...ke setiap request keportal.sekolah.test, otomatis, tanpa kode JavaScript. - Tiap request, server mencari ID itu di store. Ketemu berarti Siti. Gak ketemu atau sudah kedaluwarsa berarti
401. - Logout menghapus catatannya di store.
Max-Age=0menyuruh browser membuang cookie-nya juga.
Data sesinya ada di server. Browser cuma memegang ID-nya.
Flag di cookie-nya
HttpOnly: JavaScript di halaman gak bisa membaca cookie ini, jadi script jahat gak bisa mencurinya.Secure: cookie cuma dikirim lewat HTTPS.SameSite=Lax: cookie gak ikut di request POST yang datang dari situs lain.Max-Age=7200: berlaku 7.200 detik, alias 2 jam.
Kenapa tiap flag penting, dan apa yang terjadi tanpanya, dibahas di lesson Cookie Security di track Security.
Kode server-nya
const sessions = new Map(); // session store: id -> data sesi
function newSessionId() {
const bytes = crypto.getRandomValues(new Uint8Array(16)); // 128 bit acak
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
}
function login(user, oldId) {
if (oldId) sessions.delete(oldId); // regenerate: ID sebelum login dibuang
const id = newSessionId();
sessions.set(id, { userId: user.id, role: user.role, expiresAt: Date.now() + 7200 * 1000 });
return `sid=${id}; Path=/; Max-Age=7200; HttpOnly; Secure; SameSite=Lax`;
}
function currentUser(id) {
const session = sessions.get(id);
if (!session || session.expiresAt < Date.now()) return null; // jadi 401
return session;
}
function logout(id) {
sessions.delete(id); // server lupa, jadi cookie lama gak berguna lagi
return "sid=; Path=/; Max-Age=0";
}
crypto.getRandomValuesdengan 16 byte memberi ID 128 bit yang gak bisa ditebak. Jangan pakaiMath.randomatau angka urut.loginmembuang ID lama dan membuat ID baru. Ini regenerate: ID yang dipakai sebelum login gak pernah jadi ID orang yang sudah login. Alasannya (session fixation) ada di lesson Session Management di track Security.currentUsermengembalikannullkalau ID-nya gak ada di store atau sudah lewat waktunya. Route yang butuh login menjawab401.logoutmenghapus catatan di server. Menghapus cookie di browser saja gak cukup: siapa pun yang masih memegang ID lama tetap dianggap login.
Satu server, dua server
Store di memori cuma diketahui server yang menyimpannya. Siti login di server A, jadi catatannya ada di memori A. Request berikutnya mendarat di server B, B mencari ID itu, gak ketemu, lalu menjawab 401. Jadi Siti dianggap belum login. Itu jawaban tebakannya.
Ada dua jalan keluar:
| Cara | Kerjanya | Catatan |
|---|---|---|
| Store bersama | Semua server membaca store yang sama: Redis atau database | Cara yang umum dipakai |
| Sticky session | Load balancer selalu mengirim satu browser ke server yang sama | Server itu restart, semua sesinya hilang |
Pilihan store-nya: memori (cepat, tapi hilang saat restart dan gak terbagi), file (sederhana, satu server), database (bertahan, terbagi, sedikit lebih lambat), Redis (cepat dan terbagi, butuh server Redis).
Di Laravel
Laravel sudah menyediakan semuanya. Login dan logout yang benar:
public function login(Request $request)
{
$credentials = $request->validate(['email' => 'required|email', 'password' => 'required']);
if (! Auth::attempt($credentials)) {
return back()->withErrors(['email' => 'Email atau password salah']);
}
$request->session()->regenerate(); // ID baru setelah login
return redirect()->intended('/rapor');
}
public function logout(Request $request)
{
Auth::logout();
$request->session()->invalidate(); // hapus sesi di store
$request->session()->regenerateToken(); // token CSRF baru
return redirect('/');
}
Menyimpan dan membaca data sesi sendiri:
session()->put('kelas', 'XI IPA 2'); // atau session()->put(['kelas' => 'XI IPA 2'])
session()->get('kelas'); // 'XI IPA 2'
session()->forget('kelas');
session()->flash('status', 'Nilai tersimpan'); // cuma untuk request berikutnya
Pengaturannya di config/session.php: driver (default database, bisa redis atau file), lifetime 120 menit, http_only true, same_site lax, dan secure dari SESSION_SECURE_COOKIE. Nama cookie-nya laravel-session, dan isinya dienkripsi Laravel sebelum dikirim.
⚠️ Jebakan yang sering ketemu
- Logout cuma menghapus cookie. Catatan di store masih hidup. Hapus di server (
invalidate). - Lupa regenerate setelah login. ID sebelum login ikut jadi ID orang yang sudah login.
- Store di memori dengan lebih dari satu server. User terlempar keluar acak tiap kali request mendarat di server lain, dan semua orang logout setiap deploy.
- Menyimpan data user di cookie, bukan di server. Cookie cuma berisi ID. Data seperti role ada di store, supaya gak bisa diubah dari browser.
TL;DR: Session-based auth: server menyimpan data sesi di store (memori, file, database, Redis), browser cuma memegang session ID acak di cookie HttpOnly; Secure; SameSite=Lax. Tiap request, server mencari ID di store: ketemu berarti login, gak ketemu berarti 401. Regenerate ID saat login, hapus catatannya di server saat logout. Lebih dari satu server butuh store bersama seperti Redis, atau sticky session. Di Laravel: Auth::attempt, session()->regenerate(), session()->invalidate(), dan session()->put() untuk menyimpan data.