Kini kita punya dua cara menerbitkan bukti login: session cookie dan token. Keduanya baru berguna bila ada satu tempat yang memeriksa bukti itu secara konsisten — dan menempelkan pemeriksaan di dalam setiap handler adalah resep kelalaian: satu endpoint yang lupa diperiksa menjadi pintu tanpa kunci. Middleware autentikasi memindahkan pemeriksaan ke satu function yang berdiri di depan rute-rute yang membutuhkannya. Pada chapter ini kita membangun pola itu di atas node:http mentah, memakai header Authorization dengan skema Bearer dan status yang tepat saat penolakan.
Pola Checkpoint di Depan Handler #
Middleware yang kita kenal dari Section Framework dan Middleware berbentuk function (req, res, next). Pada node:http mentah, bentuk paling sederhana yang setara adalah function yang menerima req dan res, lalu mengembalikan salah satu dari dua hal: data pengguna bila pemeriksaan lulus, atau null setelah mengirim response penolakan sendiri. Handler tinggal memanggilnya di baris pertama dan berhenti begitu hasilnya null:
const user = wajibMasuk(req, res);
if (user === null) return; // response sudah dikirim middleware
// ... logika endpoint, user sudah pasti terautentikasi ...Satu function pemeriksa, dipanggil dengan cara yang sama di setiap endpoint terlindungi. Handler tidak lagi menyentuh header, token, ataupun status 401 — pemisahan tanggung jawab yang membuat kelalaian lebih sulit terjadi.
Header Authorization dan Skema Bearer #
Bukti token dikirim lewat header Authorization dengan format Bearer <token> — kata Bearer berarti “pembawa token ini berhak atas aksesnya”. Mem-parse header berarti memecahnya menjadi skema dan nilai, lalu memastikan skemanya memang bearer (dibandingkan tanpa peduli huruf besar-kecil, karena nama skema tidak sensitif kapital). Nilai inilah yang dicocokkan ke penyimpanan token di server.
Saat menolak permintaan, respons yang sopan menyertakan header WWW-Authenticate — petunjuk bagi klien tentang cara mengautentikasi yang diharapkan server:
| Situasi | Status | Header WWW-Authenticate |
|---|---|---|
Header Authorization tidak ada | 401 | Bearer realm="tugas" |
| Token tidak dikenal atau tidak sah | 401 | Bearer realm="tugas", error="invalid_token" |
| Token sah tetapi izin kurang | 403 | (tidak wajib) |
Satu Berkas: Endpoint Terlindung dan Terbuka #
Program berikut menyediakan endpoint /publik tanpa pemeriksaan dan /tugas yang dilindungi wajibMasuk, lalu menguji keempat skenario: tanpa header, token salah, token sah, dan endpoint publik.
import http from 'node:http';
// Token demo statis agar contoh bisa direproduksi.
// Produksi: token dibuat saat login, seperti pada Chapter Session dan Cookie.
const tokenPengguna = new Map([
['token-budi-demo', { nama: 'Budi' }],
['token-siti-demo', { nama: 'Siti' }],
]);
function ambilTokenBearer(header) {
const otorisasi = header.authorization ?? '';
const [skema, nilai] = otorisasi.split(' ');
if (skema === undefined || skema.toLowerCase() !== 'bearer' || !nilai) {
return null;
}
return nilai;
}
// Middleware autentikasi: kirim response sendiri bila gagal, kembalikan null.
function wajibMasuk(req, res) {
const token = ambilTokenBearer(req.headers);
if (token === null) {
res.writeHead(401, {
'Content-Type': 'application/json',
'WWW-Authenticate': 'Bearer realm="tugas"',
});
res.end(JSON.stringify({ error: 'Wajib ada header Authorization: Bearer <token>' }));
return null;
}
const user = tokenPengguna.get(token);
if (user === undefined) {
res.writeHead(401, {
'Content-Type': 'application/json',
'WWW-Authenticate': 'Bearer realm="tugas", error="invalid_token"',
});
res.end(JSON.stringify({ error: 'Token tidak dikenal' }));
return null;
}
return user;
}
const server = http.createServer((req, res) => {
const url = new URL(req.url, `http://127.0.0.1`);
if (req.method === 'GET' && url.pathname === '/publik') {
res.writeHead(200, { 'Content-Type': 'application/json' });
return res.end(JSON.stringify({ pesan: 'halo, tamu' }));
}
if (req.method === 'GET' && url.pathname === '/tugas') {
const user = wajibMasuk(req, res);
if (user === null) return; // middleware sudah mengirim response
res.writeHead(200, { 'Content-Type': 'application/json' });
return res.end(JSON.stringify({ pesan: `daftar tugas milik ${user.nama}` }));
}
res.writeHead(404, { 'Content-Type': 'application/json' });
return res.end(JSON.stringify({ error: 'Endpoint tidak ditemukan' }));
});
server.listen(0, '127.0.0.1', async () => {
const basis = `http://127.0.0.1:${server.address().port}`;
const r1 = await fetch(`${basis}/tugas`);
console.log('tanpa header :', r1.status, '| WWW-Authenticate:', r1.headers.get('www-authenticate'));
await r1.text();
const r2 = await fetch(`${basis}/tugas`, {
headers: { Authorization: 'Bearer token-salah' },
});
console.log('token tak dikenal:', r2.status, '| WWW-Authenticate:', r2.headers.get('www-authenticate'));
await r2.text();
const r3 = await fetch(`${basis}/tugas`, {
headers: { Authorization: 'Bearer token-budi-demo' },
});
console.log('token valid :', r3.status, '|', await r3.text());
const r4 = await fetch(`${basis}/publik`);
console.log('endpoint publik :', r4.status, '|', await r4.text());
server.close();
});Output yang Dihasilkan:
tanpa header : 401 | WWW-Authenticate: Bearer realm="tugas"
token tak dikenal: 401 | WWW-Authenticate: Bearer realm="tugas", error="invalid_token"
token valid : 200 | {"pesan":"daftar tugas milik Budi"}
endpoint publik : 200 | {"pesan":"halo, tamu"}Mari kita bedah baris-per-baris bagian intinya:
- Baris 10-17 (
ambilTokenBearer): header diurai sekali di satu tempat. Operator??mengubah header yang absen menjadi string kosong, dan tiga syarat — skema ada, skemanyabearer, nilainya tidak kosong — mengembalikannulluntuk semua bentuk header yang tidak layak. - Baris 20-40 (
wajibMasuk): dua titik tolak401yang berbeda alasannya. Baris 23-28 menolak header yang hilang atau salah format; baris 32-38 menolak token yang formatnya benar tetapi tidak dikenal. Keduanya menyertakanWWW-Authenticateagar klien tahu cara memperbaiki diri. - Baris 50-55 (handler
/tugas): pola checkpoint lengkap dalam tiga baris — panggil middleware, berhenti bilanull, lanjutkan denganuseryang sudah pasti sah. Handler hanya menyentuh data tugas, bukan mekanisme autentikasi. - Baris 61-83 (penguji): empat permintaan berurutan memetakan seluruh matriks perilaku.
r1danr2membuktikan kedua jenis401;r3membuktikan token sah sampai ke isi response;r4membuktikan endpoint publik tidak tersentuh pemeriksaan.
Middleware wajibMasuk menjawab siapa pengirimnya. Sebagian endpoint membutuhkan jawaban atas pertanyaan kedua — boleh tidak pengguna itu melakukan aksi ini — dan itulah materi Chapter Role-Based Access Control yang membangun langsung di atas berkas ini.
Rujukan protokol untuk komponen yang dipakai: header Authorization dan header WWW-Authenticate di MDN, serta status 401 .