Lewati ke konten

Praktik 10: NextAuth

InternalSebagian terujiArti badge ini

Materi Praktik 10 mengonfigurasi NextAuth dengan penyedia Credentials, lalu menyambungkannya ke fungsi verifikasi dan pembuatan token dari Praktik 9.

Berkas yang diperiksa, semuanya di Praktik 10/Konfigurasi NextAuth/File Latihan/:

route.js, providers.js, proxy.js, layout.js, theme.js, dan package.json.

P10-1. verifyAccessToken dipanggil tanpa diimpor (Kritikal)

Section titled “P10-1. verifyAccessToken dipanggil tanpa diimpor (Kritikal)”

route.js hanya mengimpor dua nama:

import { verifyCredentials } from "../../../../../lib/auth/verifyCredentials";
import { signAccessToken } from "../../../../../lib/auth/jwt";

Tetapi di dalam callback jwt, baris 46 memanggil nama ketiga:

try {
verifyAccessToken(token.accessToken); // Cek validitas
} catch (err) {
token.accessToken = signAccessToken({
user_id: token.id,
email: token.email,
role: token.role,
});
}

verifyAccessToken tidak pernah diimpor, sehingga setiap permintaan sesi melempar ReferenceError, dan catch menangkapnya.

Akibatnya accessToken dibuat ulang pada setiap permintaan, bukan hanya saat sudah kedaluwarsa. Login tetap berhasil, sehingga bug ini tidak terlihat kecuali ada yang memperhatikan tokennya terus berganti. Blok catch yang seharusnya menangani token kedaluwarsa berubah menjadi jalur yang selalu dijalankan.

P10-2. session.user.user_id selalu undefined (Tinggi)

Section titled “P10-2. session.user.user_id selalu undefined (Tinggi)”

Callback jwt mengisi token.id:

token.id = user.user_id;

Callback session membacanya dari kunci yang berbeda:

session.user.id = token.id;
session.user.user_id = token.user_id;

token.user_id tidak pernah ditulis di mana pun, sehingga session.user.user_id selalu undefined. Kode yang membaca session.user.user_id akan menerima undefined tanpa pesan galat apa pun.

P10-3. accessToken ikut terkirim ke browser (Tinggi)

Section titled “P10-3. accessToken ikut terkirim ke browser (Tinggi)”
async session({ session, token }) {
session.user.role = token.role;
session.accessToken = token.accessToken;
return session;
}

Nilai yang dikembalikan callback session dikirim ke klien dan bisa dibaca lewat /api/auth/session. Ini menempatkan bearer token aplikasi di jangkauan JavaScript halaman, dan menghapus salah satu manfaat menyimpannya di dalam cookie httpOnly.

Satu temuan yang sama sudah tercatat pada daftar bug modul di troubleshooting.md nomor 36.

P10-4. crypto sebagai dependensi npm (Sedang)

Section titled “P10-4. crypto sebagai dependensi npm (Sedang)”

package.json baris 23 memuat "crypto": "^1.0.1". Paket itu deprecated dan tidak dipakai di mana pun pada kode praktik ini.

P10-5. pages.signIn bergantung pada NEXTAUTH_URL (Sedang)

Section titled “P10-5. pages.signIn bergantung pada NEXTAUTH_URL (Sedang)”

providers.js memakai basePath="/portal/api/auth" dan proxy.js memakai basePath="/portal". Dua nilai itu memang berbeda dan benar: yang pertama letak endpoint NextAuth, yang kedua letak halaman aplikasi.

Yang perlu diperhatikan, pages: { signIn: "/login" } akan menjadi /portal/login. NextAuth menghitung alamat pengalihan dari NEXTAUTH_URL, sehingga nilai itu harus memuat /portal.

Praktik 11 memakai dua bentuk untuk variabel yang sama:

Lokasi Nilai
.env.example NEXTAUTH_URL=http://IP_EKSTERNAL_VM/portal/
Tahap 13 bagian 3 NEXTAUTH_URL=https://SUBDOMAIN/portal

Perbedaan slash di akhir cukup untuk membuat callbackUrl berbeda.

P10-6. check-config.mjs butuh paket yang tidak terpasang (Sedang)

Section titled “P10-6. check-config.mjs butuh paket yang tidak terpasang (Sedang)”

Berkas scripts/check-config.mjs pada Praktik 11 memuat:

const YAML = createRequire(import.meta.url)('yaml');

Paket yaml tidak ada di dependencies maupun devDependencies pada package.json. Perintah node scripts/check-config.mjs yang diminta materi akan berhenti dengan Error: Cannot find module 'yaml'.

Node 24 juga tidak punya parser YAML bawaan, jadi tidak ada cadangan.

Konfigurasi NextAuth.docx berisi 10 baris dan diakhiri dua baris yang hanya memuat huruf A. Bagian penutupnya kosong.

Untuk P10-1, verifyAccessToken tanpa impor

Section titled “Untuk P10-1, verifyAccessToken tanpa impor”

Tambahkan nama itu pada baris impor yang sudah ada:

import { signAccessToken, verifyAccessToken } from "../../../../../lib/auth/jwt";

Ini satu-satunya perubahan yang benar-benar memblokir. Tanpa perbaikan ini, callback jwt melempar ReferenceError pada setiap permintaan sesi, dan blok catch yang seharusnya menangani token kedaluwarsa berubah menjadi jalur yang selalu dijalankan.

Sekaligus perbaiki alur di dalam callback agar token hanya dibuat ulang bila memang sudah tidak sah:

if (!token.accessToken) {
token.accessToken = signAccessToken({ user_id: token.id, email: token.email, role: token.role });
return token;
}
try {
verifyAccessToken(token.accessToken);
} catch {
token.accessToken = signAccessToken({ user_id: token.id, email: token.email, role: token.role });
}

Cara memastikan perbaikannya bekerja: pantau nilai accessToken pada dua permintaan berturut. Sebelum diperbaiki nilainya selalu berubah. Sesudah diperbaiki nilainya tetap sampai tokennya benar-benar kedaluwarsa.

Untuk P10-2, session.user.user_id selalu undefined

Section titled “Untuk P10-2, session.user.user_id selalu undefined”

Isi kuncinya saat login, lalu beri cadangan saat dibaca:

// di callback jwt, saat pertama kali login
token.id = user.user_id;
token.user_id = user.user_id;
// di callback session
session.user.user_id = token.user_id ?? token.id;

Berkas route.js memuat versi lengkapnya.

Untuk P10-3, accessToken terkirim ke browser

Section titled “Untuk P10-3, accessToken terkirim ke browser”

Keluarkan dari callback session:

async session({ session, token }) {
session.user.id = token.id;
session.user.user_id = token.user_id ?? token.id;
session.user.email = token.email;
session.user.role = token.role;
// accessToken tidak dimasukkan ke session
return session;
}

Nilai yang dikembalikan callback session dikirim ke klien lewat /api/auth/session, sehingga apa pun yang ditaruh di sana dapat dibaca JavaScript halaman. Bila komponen klien benar-benar membutuhkan token, sediakan route terpisah yang memeriksa sesi lebih dahulu, dan jangan kirim tokennya ke komponen.

Hapus dari package.json:

"crypto": "^1.0.1",

Di kode, pakai modul bawaan Node:

import { randomUUID } from "node:crypto";

Pilih satu bentuk dan pakai di semua tempat. Karena aplikasi dilayani di /portal, nilai yang benar memuat path itu:

NEXTAUTH_URL=http://IP_STATIS_VM/portal/

Saat HTTPS aktif, ganti menjadi:

NEXTAUTH_URL=https://SUBDOMAIN/portal/

Perhatikan hubungan antara nilai ini dan cara aplikasi diakses. NextAuth menentukan nama cookie dari protokol pada NEXTAUTH_URL, seperti terlihat pada source next-auth versi 4:

secureCookie = process.env.NEXTAUTH_URL?.startsWith("https://") ?? !!process.env.VERCEL;
cookieName = secureCookie ? "__Secure-next-auth.session-token" : "next-auth.session-token";

Bila NEXTAUTH_URL sudah https:// sedangkan aplikasi masih dibuka lewat http://, browser menolak mengirim cookie bertanda Secure, dan gejalanya adalah sesi yang tampak tidak pernah login tanpa pesan galat apa pun.

Untuk P10-6, check-config.mjs butuh paket yaml

Section titled “Untuk P10-6, check-config.mjs butuh paket yaml”

Dua pilihan. Pertama, tambahkan paketnya:

Terminal window
npm install --save-dev yaml

Kedua, ganti skripnya dengan versi tanpa dependensi. Pilihan kedua lebih aman, karena skrip itu dimaksudkan untuk memeriksa konfigurasi sebelum commit, dan tidak seharusnya menuntut pemasangan tambahan lebih dahulu.

Berkas periksa-yaml.mjs adalah penggantinya, dengan keluaran setara dan satu pemeriksaan tambahan: setiap variabel ${...} pada compose harus punya nilai di .env.example.

Bagian penutup Konfigurasi NextAuth.docx masih berupa dua baris berisi huruf A. Bagian itu perlu ditulis atau dihapus. Selama masih ada, pembaca tidak tahu apakah dokumennya lengkap.

Berkas Isi
Praktik 10/perbaikan/route.js Route NextAuth dengan impor lengkap dan sesi yang benar
Praktik 10/tools/periksa-konfigurasi.mjs Periksa proxy.js, basePath, .env, dan dependensi
Praktik 11/tools/periksa-yaml.mjs Pengganti check-config.mjs tanpa dependensi
Butir Tingkat Inti
P10-1 Kritikal verifyAccessToken dipanggil tanpa diimpor
P10-2 Tinggi session.user.user_id selalu undefined
P10-3 Tinggi accessToken ikut terkirim ke browser
P10-4 Sedang Dependensi crypto tidak terpakai
P10-5 Sedang NEXTAUTH_URL ditulis dalam dua bentuk berbeda
P10-6 Sedang check-config.mjs butuh paket yang tidak terpasang
P10-7 Rendah Dokumen materi belum selesai