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:

text Kerangka handler + middleware (ilustrasi)
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:

SituasiStatusHeader WWW-Authenticate
Header Authorization tidak ada401Bearer realm="tugas"
Token tidak dikenal atau tidak sah401Bearer realm="tugas", error="invalid_token"
Token sah tetapi izin kurang403(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.

js
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:

text
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:

  1. Baris 10-17 (ambilTokenBearer): header diurai sekali di satu tempat. Operator ?? mengubah header yang absen menjadi string kosong, dan tiga syarat — skema ada, skemanya bearer, nilainya tidak kosong — mengembalikan null untuk semua bentuk header yang tidak layak.
  2. Baris 20-40 (wajibMasuk): dua titik tolak 401 yang berbeda alasannya. Baris 23-28 menolak header yang hilang atau salah format; baris 32-38 menolak token yang formatnya benar tetapi tidak dikenal. Keduanya menyertakan WWW-Authenticate agar klien tahu cara memperbaiki diri.
  3. Baris 50-55 (handler /tugas): pola checkpoint lengkap dalam tiga baris — panggil middleware, berhenti bila null, lanjutkan dengan user yang sudah pasti sah. Handler hanya menyentuh data tugas, bukan mekanisme autentikasi.
  4. Baris 61-83 (penguji): empat permintaan berurutan memetakan seluruh matriks perilaku. r1 dan r2 membuktikan kedua jenis 401; r3 membuktikan token sah sampai ke isi response; r4 membuktikan 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 .