I menu di navigazione che tengono conto delle autorizzazioni migliorano la chiarezza, ma la sicurezza deve risiedere nel backend. Scopri pattern semplici per ruoli, policy e nascondere l'interfaccia in modo sicuro.

Quando le persone dicono “nascondi il pulsante”, di solito intendono una di due cose: ridurre il disordine per utenti che non possono usare una funzione, oppure fermare gli abusi. Solo il primo obiettivo è realistico sul frontend.
I menu sensibili alle autorizzazioni sono principalmente uno strumento UX. Aiutano chi apre l'app a vedere subito cosa può fare, senza imbattersi in schermate “Accesso negato” a ogni seconda clic. Riducendo confusione come “Dove approvo le fatture?” o “Perché questa pagina dà errore?” si abbassa anche il carico sul supporto.
Nascondere l'interfaccia non è sicurezza. È chiarezza.
Anche un collega curioso può comunque:
Quindi il vero problema che i menu risolvono è guidare onestamente l'utente. Mantengono l'interfaccia allineata al lavoro, al ruolo e al contesto dell'utente, rendendo evidente quando qualcosa non è disponibile.
Un buon stato finale somiglia a questo:
Esempio: in un piccolo CRM, un Sales Rep dovrebbe vedere Leads e Tasks, ma non User Management. Se incolla comunque l'URL di user management, la pagina dovrebbe fallire chiudendosi e il server dovrebbe bloccare ogni tentativo di elencare utenti o cambiare ruoli.
La visibilità è quello che l'interfaccia sceglie di mostrare. L'autorizzazione è quello che il sistema permetterà realmente quando una richiesta arriva al server.
I menu sensibili alle autorizzazioni riducono la confusione. Se qualcuno non sarà mai autorizzato a vedere Billing o Admin, nascondere quegli elementi mantiene l'app pulita e riduce i ticket di supporto. Ma nascondere un pulsante non è una serratura. Le persone possono comunque provare l'endpoint sottostante usando strumenti di sviluppo, un vecchio bookmark o una richiesta copiata.
Una regola pratica: decidete quale esperienza volete offrire, poi applicate la regola nel backend qualunque cosa faccia la UI.
Quando decidete come presentare un'azione, tre pattern coprono la maggior parte dei casi:
“Puoi visualizzare ma non modificare” è comune e vale la pena progettarlo esplicitamente. Trattalo come due permessi: uno per leggere i dati e uno per modificarli. Nel menu potresti mostrare i dettagli del Cliente a chiunque possa leggere, ma mostrare Edit customer solo a chi ha accesso in scrittura. Nella pagina, rendi i campi in sola lettura e proteggi i controlli di modifica, permettendo comunque il caricamento della pagina.
La cosa più importante: il backend decide l'esito finale. Anche se la UI nasconde tutte le azioni admin, il server deve comunque verificare i permessi su ogni richiesta sensibile e restituire una risposta chiara “non consentito” quando qualcuno ci prova.
Il modo più rapido per rilasciare menu sensibili alle autorizzazioni è partire da un modello che il tuo team riesce a spiegare in una frase. Se non riuscite a spiegarlo, non lo manterrete corretto.
Usate i ruoli per raggruppare, non per dare significato. Admin e Support sono contenitori utili. Ma quando i ruoli cominciano a moltiplicarsi (Admin-West-Coast-ReadOnly), l'interfaccia diventa un labirinto e il backend diventa un gioco di indovinelli.
Preferite i permessi come fonte di verità per ciò che qualcuno può fare. Manteneteli piccoli e basati su azioni, come invoice.create o customer.export. Questo scala meglio della proliferazione di ruoli perché le nuove funzionalità di solito aggiungono nuove azioni, non nuovi titoli di lavoro.
Poi aggiungete policy (regole) per il contesto. Qui gestite “può modificare solo il proprio record” o “può approvare fatture solo sotto $5.000”. Le policy vi evitano di creare decine di permessi quasi duplicati che differiscono solo per una condizione.
Un layering mantenibile assomiglia a questo:
La nomenclatura conta più di quanto si pensi. Se la tua UI dice Export Customers ma l'API usa download_all_clients_v2, finirai per nascondere la cosa sbagliata o bloccare quella giusta. Mantieni i nomi umani, coerenti e condivisi tra frontend e backend:
noun.verb (o resource.action) in modo coerenteEsempio: in un CRM, un ruolo Sales potrebbe includere lead.create e lead.update, ma una policy limita gli aggiornamenti ai lead di proprietà dell'utente. Questo mantiene il menu chiaro mentre il backend resta rigoroso.
I menu sensibili alle autorizzazioni fanno sentire bene perché riducono il disordine e prevengono clic accidentali. Ma sono utili solo se il backend resta al comando. Pensate alla UI come un suggerimento e al server come il giudice.
Iniziate scrivendo cosa state proteggendo. Non pagine, ma azioni. Visualizzare la lista clienti è diverso da esportare clienti o cancellare un cliente. Questa è la spina dorsale dei menu che non si trasformano in teatro di sicurezza.
canEditCustomers, canDeleteCustomers, canExport o una lista compatta di stringhe di permesso. Tenetelo minimale.Una regola importante: non fidatevi mai dei flag di ruolo o permesso forniti dal client. La UI può nascondere pulsanti basandosi sulle capabilities, ma l'API deve comunque rifiutare le richieste non autorizzate.
I menu sensibili alle autorizzazioni dovrebbero aiutare le persone a trovare cosa possono fare, non fingere di far rispettare la sicurezza. Il frontend è un corrimano; il backend è la serratura.
Invece di spargere controlli su ogni pulsante, definisci la navigazione da una singola config che include il permesso richiesto per ogni voce, poi rendi da quella config. Questo mantiene le regole leggibili ed evita controlli dimenticati in angoli strani dell'interfaccia.
Un pattern semplice assomiglia a questo:
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));
Preferite nascondere intere sezioni (come Admin) invece di spargere controlli su ogni singolo link admin. Sono meno punti dove sbagliare.
Nascondi elementi quando l'utente non sarà mai autorizzato a usarli. Disattiva elementi quando l'utente ha il permesso, ma manca il contesto.
Esempio: Delete contact dovrebbe essere disattivato finché non è selezionato un contatto. Stesso permesso, manca solo il contesto. Quando disattivi, aggiungi un breve “perché” vicino al controllo (tooltip, testo di aiuto o nota inline): Seleziona un contatto per eliminarlo.
Un insieme di regole che regge:
Nascondere le voci aiuta le persone a concentrarsi, ma non protegge nulla. Il backend deve essere il giudice finale perché le richieste possono essere riprodotte, modificate o generate fuori dalla UI.
Una buona regola: ogni azione che cambia dati ha bisogno di un controllo di autorizzazione, in un unico punto, che ogni richiesta attraversa. Può essere middleware, un wrapper per gli handler, o un piccolo layer di policy che chiami all'inizio di ogni endpoint. Scegli un approccio e mantienilo, altrimenti perderai dei percorsi.
Mantieni l'autorizzazione separata dalla validazione dell'input. Prima decidi, “questo utente può farlo?”, poi valida il payload. Se validi prima, puoi rivelare dettagli (come quali ID di record esistono) a qualcuno che non dovrebbe nemmeno sapere che l'azione è possibile.
Un pattern che scala:
Can(user, "invoice.delete", invoice)).Usa codici di stato che aiutino sia il frontend sia i log:
401 Unauthorized quando il chiamante non è autenticato.403 Forbidden quando è autenticato ma non autorizzato.Fai attenzione al 404 Not Found come camuffamento. Può essere utile per non rivelare l'esistenza di una risorsa, ma se lo usi in modo incoerente il debugging diventa difficile. Scegli una regola coerente per tipo di risorsa.
Assicurati che la stessa autorizzazione giri sia che l'azione provenga da un click UI, un'app mobile, uno script o una chiamata diretta all'API.
Infine, registra i tentativi negati per debugging e audit, ma proteggi i log. Registra chi, quale azione e quale tipo di risorsa a livello alto. Evita campi sensibili, payload completi o segreti.
La maggior parte dei bug di permessi emergono quando gli utenti fanno cose che il menu non prevedeva. Per questo i menu sono utili, ma solo se progettate anche i percorsi che li bypassano.
Se il menu nasconde Billing per un ruolo, un utente può comunque incollare un URL salvato o aprirlo dalla cronologia del browser. Tratta ogni caricamento di pagina come una richiesta nuova: recupera i permessi correnti dell'utente e fai sì che la schermata rifiuti il caricamento di dati protetti quando manca il permesso. Un messaggio amichevole “Non hai accesso” va bene, ma la protezione reale è che il backend non restituisce nulla.
Chiunque può chiamare la tua API da dev tools, uno script o un altro client. Quindi controlla i permessi su ogni endpoint, non solo sulle schermate admin. Il rischio facile da perdere sono le azioni bulk: un singolo /items/bulk-update può accidentalmente permettere a un non-admin di cambiare campi che non vedono nell'interfaccia.
I ruoli possono anche cambiare a sessione iniziata. Se un admin rimuove un permesso, l'utente potrebbe avere ancora un token vecchio o stato menu in cache. Usa token a breve scadenza o lookup dei permessi server-side e gestisci le risposte 401/403 aggiornando le capabilities e l'interfaccia.
I dispositivi condivisi creano un altro problema: lo stato del menu in cache può trapelare tra account. Memorizza la visibilità del menu indicizzata per user ID, o evita del tutto di persisterla.
Cinque test da eseguire prima del rilascio:
Immagina un CRM interno con tre ruoli: Sales, Support e Admin. Tutti fanno login e l'app mostra un menu a sinistra, ma il menu è solo una comodità. La sicurezza reale è quello che il server permette.
Ecco un semplice set di permessi che resta leggibile:
L'interfaccia inizia chiedendo al backend le azioni consentite per l'utente corrente (spesso come lista di stringhe di permesso) più contesto base come user id e team. Il menu si costruisce da quello. Se non hai billing.view, non vedi Billing. Se hai leads.export, vedi un pulsante Export nella schermata Leads. Se puoi solo modificare i tuoi lead, il pulsante Edit può comunque apparire, ma dovrebbe essere disattivato o mostrare un messaggio chiaro quando il lead non è tuo.
Ora la parte importante: ogni endpoint d'azione applica le stesse regole.
Esempio: Sales può creare lead e modificare lead che possiede. Support può visualizzare ticket e assegnarli, ma non tocca billing. Admin può gestire utenti e billing.
Quando qualcuno prova a cancellare un lead, il backend controlla:
leads.delete?lead.owner_id == user.id?Anche se un utente Support chiama manualmente l'endpoint delete, riceve una risposta forbidden. La voce nascosta nel menu non era mai stata la protezione. La decisione del backend lo era.
La trappola più grande con i menu sensibili alle autorizzazioni è pensare di aver finito quando il menu sembra a posto. Nascondere pulsanti riduce la confusione, ma non riduce il rischio.
Errori che emergono più spesso:
isAdmin per tutto. È veloce all'inizio, poi si propaga. Presto ogni eccezione diventa un caso speciale e nessuno riesce più a spiegare le regole di accesso.role, isAdmin o permissions dal browser come verità. Derivate identity e access dal vostro token o sessione, poi lookup di ruoli e permessi server-side.Esempio concreto: nascondi la voce Export leads per i non-manager. Se l'endpoint di export non verifica i permessi, qualsiasi utente che indovina la richiesta (o la copia da un collega) può comunque scaricare il file.
Prima di distribuire i menu sensibili alle autorizzazioni, fai un ultimo giro concentrato su cosa gli utenti possono davvero fare, non su cosa possono vedere.
Attraversa l'app come ogni ruolo principale e prova le stesse azioni. Fallo sia nell'interfaccia sia chiamando direttamente l'endpoint (o usando dev tools) per assicurarti che il server sia la fonte di verità.
Checklist:
Un modo pratico per individuare gap: scegli un pulsante “pericoloso” (elimina utente, esporta CSV, cambia billing) e traccialo end-to-end. La voce di menu dovrebbe essere nascosta quando appropriato, l'API dovrebbe rifiutare chiamate non autorizzate e la UI dovrebbe recuperare con grazia quando riceve un 403.
Parti piccolo. Non serve una matrice di accesso perfetta al giorno uno. Scegli un gruppo ristretto di azioni che contano di più (view, create, edit, delete, export, manage users), mappale ai ruoli che già hai e procedi. Quando arriva una nuova funzionalità, aggiungi solo le nuove azioni che introduce.
Prima di costruire schermate, fai un rapido passaggio di pianificazione che elenchi azioni, non pagine. Una voce di menu come Invoices nasconde molte azioni: view list, view details, create, refund, export. Scriverle prima rende sia l'interfaccia che le regole backend più chiare e previene l'errore comune di limitare una pagina lasciando un endpoint rischioso non protetto.
Quando refattorizzi le regole di accesso, trattalo come qualsiasi altro cambiamento rischioso: tieni una rete di sicurezza. Gli snapshot ti permettono di confrontare il comportamento prima e dopo. Se un ruolo perde improvvisamente accesso o ne acquisisce di indesiderato, il rollback è più veloce che correggere in produzione mentre gli utenti sono bloccati.
Una routine di rilascio semplice aiuta i team a muoversi velocemente senza indovinare:
Se costruisci con una piattaforma chat-based come Koder.ai (koder.ai), la stessa struttura vale: tieni permessi e policy definiti una sola volta, fai leggere le capabilities all'interfaccia dal server e rendi obbligatori i controlli backend in ogni handler.
I menu sensibili alle autorizzazioni risolvono principalmente la chiarezza, non la sicurezza. Aiutano gli utenti a concentrarsi su ciò che possono effettivamente fare, riducono i clic morti e diminuiscono le richieste di supporto del tipo “perché vedo questa cosa?”.
La sicurezza deve comunque essere applicata nel backend, perché chiunque può provare link profondi, vecchi segnalibri o chiamate API dirette indipendentemente da quello che mostra l'interfaccia.
Nascondi quando una funzionalità dovrebbe essere effettivamente non scoperta da un ruolo e non c'è una via prevista per usarla.
Disattiva quando l'utente potrebbe avere accesso ma al momento manca il contesto, ad esempio nessun record selezionato, stato del form invalido o dati ancora in caricamento. Se disattivi, aggiungi una breve spiegazione così non sembra rotto.
Perché la visibilità non è autorizzazione. Un utente può incollare un URL, riutilizzare una schermata amministrativa salvata o chiamare la tua API al di fuori dell'interfaccia.
Considera l'interfaccia come guida. Considera il backend come l'ultimo decisore per ogni richiesta sensibile.
Il server dovrebbe restituire una piccola risposta di “capacità” dopo il login o al refresh di sessione, basata su controlli di autorizzazione lato server. L'interfaccia poi rende menu e pulsanti da quelle informazioni.
Non fidarti di flag forniti dal client come isAdmin; calcola i permessi dall'identità autenticata sul server.
Inizia facendo l'inventario delle azioni, non delle pagine. Per ogni funzionalità, separa operazioni come read, create, update, delete, export, invite e change billing.
Poi applica ciascuna autorizzazione nell'handler backend (o in middleware/wrapper) prima di eseguire qualsiasi lavoro. Collega il menu agli stessi nomi di permesso così UI e API restano allineate.
Una regola pratica: i ruoli sono contenitori, i permessi sono la fonte di verità. Mantieni i permessi piccoli e basati su azioni (per esempio invoice.create) e assegnali ai ruoli.
Se i ruoli cominciano a moltiplicarsi per codificare condizioni (come regione o proprietà), sposta quelle condizioni nelle policy invece di creare varianti di ruolo senza fine.
Usa le policy per regole contestuali come “modifica solo i propri record” o “approva fatture solo sotto una certa soglia”. Questo mantiene stabile la lista dei permessi e permette comunque di esprimere vincoli reali.
Il backend deve valutare la policy usando il contesto della risorsa (per esempio owner ID o org ID), non supposizioni dall'interfaccia.
Non sempre. Le letture che espongono dati sensibili o che bypassano i filtri normali dovrebbero essere protette, come export, log di audit, salari, liste utenti admin o qualsiasi endpoint che restituisce più di quanto l'interfaccia normalmente mostri.
Una buona regola: tutti gli scritti devono essere controllati, e anche le letture sensibili devono essere controllate.
Gli endpoint bulk si perdono facilmente perché possono modificare molti record o campi in una sola richiesta. Un utente bloccato nella UI potrebbe comunque chiamare /items/bulk-update direttamente.
Controlla i permessi per l'azione in blocco e valida anche quali campi possono essere cambiati per quel ruolo, altrimenti potresti permettere modifiche a campi nascosti.
Assumi che i permessi possano cambiare mentre qualcuno è connesso. Quando l'API restituisce 401 o 403, l'interfaccia dovrebbe gestirlo come uno stato normale: aggiorna le capacità, rinnova il menu e mostra un messaggio chiaro.
Evita anche di salvare lo stato del menu in modo che possa trapelare tra account su dispositivi condivisi; se fai cache, indicizzala per ID utente o non persisterla affatto.