Lewati ke konten

Perbaikan verifyBearerToken

Token hanya dari header, pesan galat seragam.

Berkas: alat/hari-3/Praktik 9/perbaikan/verifyBearerToken.js

Praktik 9/perbaikan/verifyBearerToken.js
// =====================================================================
// Praktik 9 - versi perbaikan verifyBearerToken.js
//
// Perubahan dari versi modul:
//
// 1. getTokenParams() DIHAPUS. Menerima token dari query string berarti
// token ikut tercatat di access log Nginx, log Cloud Build, history
// browser, dan header Referer saat halaman memuat resource lain.
//
// 2. requireAuth() sekarang memeriksa token lebih dahulu sebelum apa pun,
// dan mengembalikan bentuk hasil yang konsisten.
//
// 3. Ada pesan galat yang tidak membocorkan alasan sebenarnya. Versi modul
// membedakan "token tidak ditemukan" dan "token tidak valid", yang
// membantu penyerang menebak. Untuk klien cukup satu pesan 401.
//
// 4. Ada pemeriksaan bentuk payload. jwt.verify() bisa mengembalikan string
// (kalau token ditandatangani dengan secret berbentuk string), dan
// payload.role bisa tidak ada. Tanpa pemeriksaan ini, hasRequiredRole()
// akan membandingkan undefined dan mengembalikan false, tetapi pesan
// galatnya menyesatkan.
//
// Letakkan berkas ini di: src/lib/auth/verifyBearerToken.js
// =====================================================================
import { verifyAccessToken } from "./jwt";
import { hasRequiredRole } from "./roles";
// Mengambil token hanya dari header "Authorization: Bearer <token>".
// Tidak ada lagi jalur lewat query string.
export function getBearerToken(request) {
const authHeader = request.headers.get("authorization");
if (!authHeader) return null;
const [skema, nilai] = authHeader.split(" ");
if (!skema || skema.toLowerCase() !== "bearer") return null;
if (!nilai || !nilai.trim()) return null;
return nilai.trim();
}
/**
* @returns {{ payload: object } | { error: string, status: number }}
*/
export function requireAuth(request, minRole = null) {
const token = getBearerToken(request);
// Pesan dan status dibuat seragam untuk semua kegagalan autentikasi.
// Klien tidak perlu tahu apakah tokennya kosong, kadaluarsa, atau salah
// tanda tangan — yang penting dia harus login ulang.
if (!token) {
return { error: "Unauthorized", status: 401 };
}
let payload;
try {
payload = verifyAccessToken(token);
} catch {
return { error: "Unauthorized", status: 401 };
}
// jwt.verify bisa mengembalikan string untuk token dengan payload berupa
// string biasa. Pastikan bentuknya objek sebelum dipakai.
if (typeof payload !== "object" || payload === null) {
return { error: "Unauthorized", status: 401 };
}
if (typeof payload.role !== "string" || !payload.role) {
return { error: "Unauthorized", status: 401 };
}
if (minRole && !hasRequiredRole(payload.role, minRole)) {
// 403 di sini benar: tokennya sah, hanya haknya kurang.
return { error: "Forbidden: Akses ditolak", status: 403 };
}
return { payload };
}