Menu navigasi yang sadar izin meningkatkan kejelasan, tetapi keamanan harus berada di backend. Lihat pola sederhana untuk peran, kebijakan, dan penyembunyian UI yang aman.

Saat orang berkata “sembunyikan tombol”, biasanya mereka bermaksud satu dari dua hal: mengurangi kekacauan bagi pengguna yang tidak bisa menggunakan fitur, atau mencegah penyalahgunaan. Hanya tujuan pertama yang realistis di frontend.
Menu navigasi yang sadar izin terutama adalah alat UX. Mereka membantu seseorang membuka aplikasi dan segera melihat apa yang bisa mereka lakukan, tanpa terus-menerus menemui layar “Akses ditolak”. Mereka juga mengurangi beban dukungan dengan mencegah kebingungan seperti “Di mana saya menyetujui faktur?” atau “Kenapa halaman ini error?”.
Menyembunyikan UI bukanlah keamanan. Itu kejelasan.
Bahkan rekan kerja yang penasaran masih bisa:
Jadi masalah nyata yang diselesaikan menu sadar izin adalah panduan yang jujur. Mereka menjaga antarmuka selaras dengan pekerjaan, peran, dan konteks pengguna, sekaligus membuat jelas ketika sesuatu tidak tersedia.
Keadaan akhir yang baik terlihat seperti ini:
Contoh: di CRM kecil, Sales Rep harus melihat Leads dan Tasks, tapi tidak User Management. Jika mereka menempel URL manajemen pengguna, halaman harus gagal tertutup (fail closed), dan server tetap memblokir upaya untuk melist pengguna atau mengubah peran.
Visibilitas adalah apa yang antarmuka pilih untuk ditampilkan. Otorisasi adalah apa yang sistem benar-benar izinkan ketika sebuah permintaan mencapai server.
Menu sadar izin mengurangi kebingungan. Jika seseorang tidak akan pernah diizinkan melihat Billing atau Admin, menyembunyikan item-item itu membuat aplikasi lebih rapi dan menurunkan tiket dukungan. Tetapi menyembunyikan tombol bukanlah kunci. Orang masih bisa mencoba endpoint di baliknya menggunakan dev tools, bookmark lama, atau permintaan yang disalin.
Aturan praktis: putuskan pengalaman yang Anda inginkan, lalu tegakkan aturan itu di backend apapun yang dilakukan UI.
Saat memutuskan bagaimana menyajikan sebuah aksi, tiga pola menutup sebagian besar kasus:
“Bisa melihat tapi tidak mengedit” itu umum dan layak didesain secara eksplisit. Perlakukan itu sebagai dua izin: satu untuk membaca data dan satu untuk mengubahnya. Di menu, Anda mungkin menampilkan detail Customer untuk semua yang bisa baca, tapi hanya menampilkan Edit customer untuk mereka yang punya akses tulis. Di halaman, render field sebagai read-only dan batasi kontrol edit, sementara halaman tetap boleh dimuat.
Yang paling penting, backend menentukan hasil akhir. Bahkan jika UI menyembunyikan setiap aksi admin, server tetap perlu memeriksa izin pada setiap permintaan sensitif dan mengembalikan respons “tidak diizinkan” yang jelas ketika seseorang mencoba.
Cara tercepat untuk mengirim menu sadar izin adalah mulai dengan model yang tim Anda bisa jelaskan dalam satu kalimat. Jika Anda tidak bisa menjelaskannya, Anda tidak akan menjaganya tetap benar.
Gunakan peran untuk pengelompokan, bukan untuk makna. Admin dan Support berguna sebagai bucket. Tapi ketika peran mulai berkembang biak (Admin-West-Coast-ReadOnly), UI menjadi labirin dan backend menjadi tebakan-tebakan.
Lebih baik menjadikan izin sebagai sumber kebenaran untuk apa yang seseorang bisa lakukan. Buat mereka kecil dan berbasis aksi, seperti invoice.create atau customer.export. Ini lebih mudah diskalakan dibandingkan peran yang mengembang karena fitur baru biasanya menambah aksi baru, bukan jabatan kerja baru.
Lalu tambahkan kebijakan (policies) untuk konteks. Di sinilah Anda menangani “bisa mengedit hanya catatan sendiri” atau “bisa menyetujui faktur hanya di bawah $5.000.” Kebijakan mencegah Anda membuat puluhan izin yang hampir identik yang hanya berbeda oleh kondisi.
Lapisan yang terawat terlihat seperti ini:
Penamaan lebih penting dari yang diduga. Jika UI Anda mengatakan Export Customers tapi API menggunakan download_all_clients_v2, Anda pada akhirnya akan menyembunyikan hal yang salah atau memblokir hal yang benar. Jaga nama agar manusiawi, konsisten, dan dibagikan antara frontend dan backend:
noun.verb (atau resource.action) secara konsistenContoh: di CRM, peran Sales mungkin mencakup lead.create dan lead.update, tapi kebijakan membatasi update ke lead yang dimiliki pengguna. Itu menjaga menu tetap jelas sementara backend tetap ketat.
Menu sadar izin terasa baik karena mengurangi kekacauan dan mencegah klik tidak sengaja. Tapi mereka hanya membantu jika backend tetap memegang kendali. Anggap UI sebagai petunjuk, dan server sebagai hakim.
Mulailah dengan menulis apa yang Anda lindungi. Bukan halaman, tapi aksi. Melihat daftar customer berbeda dari mengekspor customer dan menghapus customer. Ini adalah tulang punggung menu navigasi sadar izin yang tidak berubah menjadi teater keamanan.
canEditCustomers, canDeleteCustomers, canExport, atau daftar string izin yang ringkas. Jaga seminimal mungkin.Aturan kecil tapi penting: jangan pernah percaya flag peran atau izin yang dikirim klien. UI bisa menyembunyikan tombol berdasarkan capabilities, tapi API tetap harus menolak permintaan yang tidak berwenang.
Menu sadar izin harus membantu orang menemukan apa yang bisa mereka lakukan, bukan pura-pura menegakkan keamanan. Frontend adalah pembatas panduan. Backend adalah kunci.
Daripada menyebar pemeriksaan izin di setiap tombol, definisikan navigasi Anda dari satu konfigurasi yang menyertakan izin yang diperlukan untuk setiap item, lalu render dari konfigurasi itu. Ini menjaga aturan tetap terbaca dan menghindari pemeriksaan yang terlupakan di sudut UI yang aneh.
Pola sederhana terlihat seperti ini:
const menu = [
{ label: "Contacts", path: "/contacts", requires: "contacts.read" },
{ label: "Export", action: "contacts.export", requires: "contacts.export" },
{ label: "Admin", path: "/admin", requires: "admin.access" },
];
const visibleMenu = menu.filter(item => userPerms.includes(item.requires));
Lebih baik menyembunyikan seluruh bagian (mis. Admin) daripada menaburkan pemeriksaan pada setiap link halaman admin. Itu lebih sedikit tempat untuk salah.
Sembunyikan item saat pengguna tidak akan pernah diizinkan menggunakannya. Nonaktifkan item saat pengguna punya izin, tapi konteks saat ini belum memadai.
Contoh: Delete contact harus dinonaktifkan sampai satu contact dipilih. Izin sama, hanya konteks yang kurang. Saat menonaktifkan, tambahkan pesan singkat “kenapa” dekat kontrol (tooltip, teks bantu, atau catatan inline): Pilih kontak untuk dihapus.
Aturan yang kuat:
Menyembunyikan item menu membantu orang fokus, tapi tidak melindungi apa pun. Backend harus menjadi hakim akhir karena permintaan dapat diputar ulang, diedit, atau dipicu di luar UI Anda.
Aturan yang baik: setiap aksi yang mengubah data membutuhkan satu pemeriksaan otorisasi, di satu tempat, yang dilewati oleh setiap permintaan. Itu bisa berupa middleware, wrapper handler, atau lapisan kebijakan kecil yang Anda panggil di awal setiap endpoint. Pilih satu pendekatan dan patuhi, atau jalur akan terlewat.
Jaga otorisasi terpisah dari validasi input. Pertama putuskan, “apakah pengguna ini diizinkan melakukan ini?”, lalu validasi payload. Jika Anda memvalidasi dulu, Anda bisa membocorkan detail (seperti apakah record ID ada) kepada seseorang yang bahkan tidak seharusnya tahu aksi itu mungkin.
Pola yang skalabel:
Can(user, "invoice.delete", invoice)).Gunakan kode status yang membantu frontend dan log Anda:
401 Unauthorized ketika pemanggil belum login.403 Forbidden ketika sudah login tapi tidak diizinkan.Berhati-hatilah dengan 404 Not Found sebagai penyamaran. Itu bisa berguna untuk menghindari mengungkapkan bahwa sebuah resource ada, tapi jika Anda menggunakannya secara acak, debugging menjadi menyusahkan. Pilih aturan yang konsisten per tipe resource.
Pastikan otorisasi yang sama dijalankan apakah aksi datang dari klik tombol, aplikasi mobile, skrip, atau panggilan API langsung.
Akhirnya, log upaya yang ditolak untuk debugging dan audit, tapi jaga log tetap aman. Catat siapa, aksi apa, dan tipe resource tingkat tinggi. Hindari field sensitif, payload penuh, atau rahasia.
Sebagian besar bug izin muncul ketika pengguna melakukan sesuatu yang menu Anda tidak antisipasi. Itu sebabnya menu sadar izin berguna, tapi hanya jika Anda juga mendesain jalur yang melewatinya.
Jika menu menyembunyikan Billing untuk sebuah peran, pengguna masih bisa menempel URL yang tersimpan atau membukanya dari riwayat browser. Perlakukan setiap pemuatan halaman seperti permintaan baru: ambil izin pengguna saat ini, dan biarkan layar itu menolak memuat data terlindungi saat izin hilang. Pesan ramah “Anda tidak memiliki akses” boleh, tapi perlindungan nyata adalah backend mengembalikan tidak ada data.
Siapa pun bisa memanggil API Anda dari dev tools, skrip, atau klien lain. Jadi periksa izin di setiap endpoint, bukan hanya layar admin. Risiko yang mudah terlewat adalah aksi bulk: satu /items/bulk-update bisa tanpa sengaja membiarkan non-admin mengubah field yang tidak mereka lihat di UI.
Peran juga bisa berubah saat sesi berjalan. Jika admin mencabut izin, pengguna mungkin masih punya token lama atau state menu yang di-cache. Gunakan token yang masa berlakunya pendek atau lookup izin di server, dan tangani respons 401/403 dengan me-refresh izin dan memperbarui UI.
Perangkat bersama menimbulkan jebakan lain: state menu yang di-cache bisa bocor antar akun. Simpan visibilitas menu dengan kunci user ID, atau hindari menyimpannya sama sekali.
Lima tes yang sepadan dijalankan sebelum rilis:
Bayangkan CRM internal dengan tiga peran: Tim Penjualan, Dukungan, dan Admin. Semua orang sign in dan aplikasi menampilkan menu kiri, tetapi menu hanya kenyamanan. Keamanan nyata adalah apa yang server izinkan.
Berikut set izin sederhana yang tetap terbaca:
UI mulai dengan meminta backend daftar aksi yang diizinkan untuk pengguna saat ini (sering sebagai daftar string izin) plus konteks dasar seperti user id dan tim. Menu dibangun dari itu. Jika Anda tidak punya billing.view, Anda tidak melihat Billing. Jika Anda punya leads.export, Anda melihat tombol Export di layar Leads. Jika Anda hanya bisa mengedit lead milik sendiri, tombol Edit bisa tetap muncul, tapi harus dinonaktifkan atau menunjukkan pesan jelas saat lead bukan milik Anda.
Sekarang bagian penting: setiap endpoint aksi menegakkan aturan yang sama.
Contoh: Sales bisa membuat leads dan mengedit leads yang mereka miliki. Dukungan bisa melihat tiket dan menugaskan tiket, tapi tidak boleh menyentuh billing. Admin bisa mengelola pengguna dan billing.
Saat seseorang mencoba menghapus lead, backend memeriksa:
leads.delete?lead.owner_id == user.id?Bahkan jika seorang pengguna Dukungan memanggil endpoint delete secara manual, mereka mendapat respons forbidden. Item menu yang tersembunyi tidak pernah menjadi perlindungan. Keputusan backend-lah yang melindungi.
Jebakan terbesar dengan menu sadar izin adalah mengira pekerjaan selesai saat menu terlihat benar. Menyembunyikan tombol mengurangi kebingungan, tapi tidak mengurangi risiko.
Kesalahan yang sering muncul:
isAdmin raksasa untuk semuanya. Rasanya cepat, lalu menyebar. Segera setiap pengecualian menjadi kasus khusus dan tidak ada yang bisa menjelaskan aturan akses.role, isAdmin, atau permissions dari browser sebagai kebenaran. Turunkan identitas dan akses dari sesi atau token Anda sendiri, lalu lookup peran dan izin server-side.Contoh konkret: Anda menyembunyikan menu Export leads untuk non-manager. Jika endpoint ekspor tidak juga memeriksa izin, pengguna mana pun yang menebak permintaan (atau menyalinnya dari rekan) masih bisa mengunduh file.
Sebelum Anda mengirim menu sadar izin, lakukan satu pemeriksaan terakhir fokus pada apa yang pengguna benar-benar bisa lakukan, bukan hanya apa yang mereka lihat."
Cobalah aplikasi sebagai setiap peran utama dan jalankan set aksi yang sama. Lakukan di UI dan juga dengan memanggil endpoint langsung (atau menggunakan dev tools) untuk memastikan server adalah sumber kebenaran.
Checklist:
Satu cara praktis untuk menemukan celah: pilih satu tombol “berbahaya” (hapus pengguna, ekspor CSV, ubah tagihan) dan lacak end-to-end. Item menu harus tersembunyi saat cocok, API harus menolak panggilan tidak berwenang, dan UI harus pulih dengan rapi saat mendapat 403.
Mulai kecil. Anda tidak perlu matriks akses sempurna di hari pertama. Pilih beberapa aksi yang paling penting (view, create, edit, delete, export, manage users), peta-kan ke peran yang sudah ada, dan lanjutkan. Saat fitur baru hadir, tambahkan hanya aksi baru yang diperkenalkan.
Sebelum Anda membangun layar, lakukan sesi perencanaan singkat yang mencatat aksi, bukan halaman. Item menu seperti Invoices menyembunyikan banyak aksi: lihat daftar, lihat detail, buat, refund, ekspor. Menuliskannya dulu membuat UI dan aturan backend lebih jelas, dan mencegah kesalahan umum mengunci seluruh halaman sementara endpoint berisiko tetap tidak terlindungi.
Saat merombak aturan akses, perlakukan seperti perubahan berisiko lain: sediakan jaring pengaman. Snapshot memungkinkan Anda membandingkan perilaku sebelum dan sesudah. Jika sebuah peran tiba-tiba kehilangan akses yang dibutuhkan, atau mendapat akses yang tidak seharusnya, rollback lebih cepat daripada memperbaiki produksi saat pengguna sedang terblokir.
Rutin rilis sederhana membantu tim bergerak cepat tanpa menebak-nebak:
Jika Anda membangun dengan platform berbasis chat seperti Koder.ai (koder.ai), struktur yang sama tetap berlaku: definisikan izin dan kebijakan sekali, biarkan UI membaca capabilities dari server, dan jadikan pemeriksaan backend tidak opsional di setiap handler.
Menu yang sadar izin sebagian besar menyelesaikan kejelasan, bukan keamanan. Mereka membantu pengguna fokus pada apa yang benar-benar bisa mereka lakukan, mengurangi klik yang berujung buntu, dan memangkas pertanyaan dukungan seperti “kenapa saya melihat ini?”.
Keamanan tetap harus ditegakkan di backend, karena siapa pun bisa mencoba deep link, bookmark lama, atau panggilan API langsung terlepas dari apa yang ditampilkan UI.
Sembunyikan ketika fitur seharusnya tidak mudah ditemukan untuk sebuah peran dan tidak ada jalur yang diharapkan bagi mereka untuk menggunakannya.
Nonaktifkan ketika pengguna mungkin punya akses tetapi saat ini kekurangan konteks, misalnya belum memilih record, status form tidak valid, atau data masih dimuat. Jika menonaktifkan, tambahkan penjelasan singkat agar tidak terlihat rusak.
Karena visibilitas bukan otorisasi. Seorang pengguna bisa menempelkan URL, menggunakan bookmark admin lama, atau memanggil API Anda di luar UI.
Perlakukan UI sebagai panduan. Perlakukan backend sebagai pembuat keputusan akhir untuk setiap permintaan yang sensitif.
Server Anda harus mengembalikan respons kecil berisi “kemampuan” setelah login atau refresh sesi, berdasarkan pemeriksaan izin di sisi server. UI lalu merender menu dan tombol dari data itu.
Jangan percaya flag yang dikirim dari klien seperti isAdmin; hitung izin dari identitas yang terotentikasi di server.
Mulailah dengan mendata aksi, bukan halaman. Untuk setiap fitur, pisahkan tindakan seperti baca, buat, ubah, hapus, ekspor, undang, dan perubahan tagihan.
Lalu tegakkan setiap aksi di handler backend (atau middleware/wrapper) sebelum melakukan pekerjaan apa pun. Sambungkan menu ke nama izin yang sama sehingga UI dan API tetap selaras.
Default praktis: gunakan peran sebagai wadah, dan izin sebagai sumber kebenaran. Pertahankan izin kecil dan berbasis aksi (mis. invoice.create), dan lampirkan ke peran.
Jika peran mulai berlipat untuk mengkode kondisi (mis. wilayah atau kepemilikan), pindahkan kondisi tersebut ke kebijakan daripada membuat varian peran tanpa akhir.
Gunakan kebijakan untuk aturan kontekstual seperti “bisa mengedit hanya catatan miliknya” atau “bisa menyetujui faktur di bawah batas tertentu.” Itu menjaga daftar izin Anda tetap stabil sambil mengekspresikan batasan dunia nyata.
Backend harus mengevaluasi kebijakan menggunakan konteks resource (mis. owner ID atau org ID), bukan asumsi dari UI.
Tidak selalu. Baca yang mengekspos data sensitif atau melewati penyaringan normal sebaiknya juga dibatasi, seperti ekspor, log audit, data gaji, daftar pengguna admin, atau endpoint yang mengembalikan lebih dari apa yang biasanya terlihat UI.
Batasannya: semua penulisan harus diperiksa, dan baca yang sensitif juga perlu diperiksa.
Endpoint bulk mudah terlewat karena bisa mengubah banyak record atau field sekaligus. Seorang pengguna mungkin diblokir di UI tetapi tetap memanggil /bulk-update langsung.
Periksa izin untuk aksi bulk itu sendiri, dan juga validasi field mana yang boleh diubah untuk peran tersebut, agar tidak secara tidak sengaja mengizinkan pengeditan field tersembunyi.
Asumsikan izin bisa berubah saat seseorang masih masuk. Ketika API mengembalikan 401 atau 403, UI harus menanggapinya sebagai status normal: refresh capabilities, perbarui menu, dan tampilkan pesan yang jelas.
Jangan menyimpan visibilitas menu dengan cara yang bisa bocor antar akun pada perangkat bersama; jika nge-cache, kunci berdasarkan identitas pengguna atau jangan simpan sama sekali.