7 min

Menus de navegação sensíveis a permissões que ainda reforçam o acesso

Menus sensíveis a permissões melhoram a clareza, mas a segurança precisa viver no backend. Veja padrões simples para roles, policies e esconder a UI com segurança.

Menus de navegação sensíveis a permissões que ainda reforçam o acesso

Que problema os menus sensíveis a permissões realmente resolvem

Quando as pessoas dizem “ocultar o botão”, normalmente querem uma de duas coisas: reduzir poluição para usuários que não podem usar um recurso, ou impedir uso indevido. Só o primeiro objetivo é realista no frontend.

Menus sensíveis a permissões são principalmente uma ferramenta de UX. Eles ajudam alguém a abrir o app e ver imediatamente o que pode fazer, sem trombar em telas de “Acesso negado” a cada clique. Também reduzem carga no suporte, evitando confusões como “Onde aprovo faturas?” ou “Por que esta página deu erro?”

Ocultar a UI não é segurança. É clareza.

Mesmo um colega curioso ainda pode:

  • Digitar um deep link para uma página ocultada se souber a URL
  • Abrir um bookmark antigo para uma tela de admin
  • Chamar sua API diretamente com um script ou ferramenta
  • Modificar requisições do navegador e tentar IDs diferentes

Então o problema real que menus sensíveis a permissões resolvem é orientação honesta. Eles mantêm a interface alinhada com o trabalho, papel e contexto do usuário, deixando claro quando algo não está disponível.

Um bom estado final parece com isto:

  • Usuários veem principalmente ações que fazem sentido para eles e raramente encontram erros-surpresa.
  • Desenvolvedores têm uma única fonte de verdade para regras de acesso, em vez de checagens espalhadas.
  • Mudanças de produto ficam mais seguras porque um novo item de menu força a decidir qual permissão ele exige.
  • Segurança é aplicada onde importa: o backend rejeita ações proibidas toda vez, mesmo se a UI as mostrar por engano.

Exemplo: em um CRM pequeno, um Sales Rep deve ver Leads e Tasks, mas não User Management. Se ele colar a URL de user management na barra mesmo assim, a página deve falhar fechada, e o servidor deve bloquear qualquer tentativa de listar usuários ou alterar papéis.

Visibilidade não é autorização

Visibilidade é o que a interface escolhe mostrar. Autorização é o que o sistema realmente permite quando uma requisição chega ao servidor.

Menus sensíveis a permissões reduzem confusão. Se alguém nunca terá acesso a Billing ou Admin, ocultar esses itens mantém o app limpo e reduz tickets de suporte. Mas ocultar um botão não é um cadeado. As pessoas ainda podem tentar o endpoint subjacente usando dev tools, um bookmark antigo ou uma requisição copiada.

Uma regra prática: decida qual experiência quer oferecer e, então, aplique a regra no backend independentemente do que a UI fizer.

Ao decidir como apresentar uma ação, três padrões cobrem a maioria dos casos:

  • Ocultar quando o recurso deve ser indisponível para a maioria dos papéis (por exemplo, ferramentas apenas internas).
  • Desativar quando o recurso existe, mas o usuário não pode usá-lo agora (por exemplo, Export desativado até upgrade de plano, até selecionar um registro, ou enquanto dados carregam).
  • Mostrar com explicação quando os usuários precisam entender por que não podem prosseguir (“Você pode ver faturas, mas só Owners podem editar métodos de pagamento”).

“Você pode ver mas não pode editar” é comum e vale a pena desenhar explicitamente. Trate como duas permissões: uma para leitura e outra para alteração. No menu, você pode mostrar Customer details para todos que têm leitura, mas só mostrar Edit customer para quem tem acesso de escrita. Na página, renderize campos como somente leitura e bloqueie controles de edição, enquanto ainda permite o carregamento da página.

Mais importante: o backend decide o resultado final. Mesmo que a UI oculte toda ação de admin, o servidor ainda precisa checar permissões em cada requisição sensível e retornar uma resposta clara de “não permitido” quando alguém tentar.

Escolha um modelo de permissões que você consiga manter

A forma mais rápida de entregar menus sensíveis a permissões é começar com um modelo que sua equipe consiga explicar em uma frase. Se você não consegue explicar, não vai manter correto.

Use roles para agrupar, não para dar significado. Admin e Support são buckets úteis. Mas quando roles começam a proliferar (Admin-West-Coast-ReadOnly), a UI vira um labirinto e o backend vira adivinhação.

Prefira permissões como fonte de verdade sobre o que alguém pode fazer. Mantenha-as pequenas e orientadas a ações, como invoice.create ou customer.export. Isso escala melhor que a proliferação de roles porque novos recursos normalmente adicionam novas ações, não novos cargos.

Depois, adicione policies (regras) para contexto. É aqui que você trata “pode editar apenas seu próprio registro” ou “pode aprovar faturas só abaixo de $5.000.” Políticas evitam criar dezenas de permissões quase-duplicadas que diferem apenas por uma condição.

Uma camada que dá para manter fica assim:

  • Roles agrupam permissões (função de trabalho)
  • Permissões descrevem ações (o quê)
  • Policies adicionam contexto (quando, quais registros)

Nomear importa mais do que se espera. Se sua UI diz Export Customers mas a API usa download_all_clients_v2, você acabará ocultando a coisa errada ou bloqueando a certa. Mantenha nomes humanos, consistentes e compartilhados entre frontend e backend:

  • Use noun.verb (ou resource.action) de forma consistente
  • Combine rótulos da UI com a intenção da permissão, não com nomes internos de código
  • Mantenha nomes antigos funcionando quando renomear recursos

Exemplo: em um CRM, um papel Sales pode incluir lead.create e lead.update, mas uma policy limita updates a leads que o usuário possui. Isso mantém seu menu claro enquanto o backend fica rígido.

Passo a passo: implemente roles e permissões de ponta a ponta

Menus sensíveis a permissões dão uma boa experiência porque reduzem poluição e evitam cliques acidentais. Mas só ajudam se o backend mantiver o controle. Pense na UI como uma dica e no servidor como o juiz.

Comece registrando o que você está protegendo. Não páginas, mas ações. Ver lista de clientes é diferente de exportar clientes e diferente de deletar cliente. Isso é a espinha dorsal de menus sensíveis a permissões que não viram teatro de segurança.

Uma receita prática de ponta a ponta

  1. Inventarie ações na UI e na API. Para cada recurso, liste operações concretas: read, create, update, delete, export, invite, change billing, run admin reports.
  2. Defina permissões no servidor como fonte de verdade. Armazene o mapeamento role->permissão no banco (ou config) e faça cada handler da API perguntar “este usuário tem a permissão X sobre o recurso Y?”.
  3. Retorne um pequeno payload de capacidades para a UI. Após login (ou ao atualizar a sessão), retorne booleans como canEditCustomers, canDeleteCustomers, canExport, ou uma lista compacta de strings de permissão. Mantenha mínimo.
  4. Aplique checagens em toda escrita, e em leituras sensíveis. Todas as mutações devem checar permissões. Algumas leituras também (por exemplo, salários, audit logs, exports ou qualquer endpoint que ignore filtros normais).
  5. Adicione alguns testes focados em roles. Escolha 2–3 papéis chave e teste as ações de maior risco. Exemplo: Sales pode criar deals mas não exportar clientes; Admin pode convidar usuários.

Uma regra pequena mas importante: nunca confie em roles ou flags enviadas pelo cliente. A UI pode ocultar botões com base nas capacidades, mas a API ainda deve rejeitar requisições não autorizadas.

Padrões de frontend que permanecem honestos

Start from a concrete example
Spin up a simple CRM with roles, menus, and strict API checks as a baseline.

Menus sensíveis a permissões devem ajudar as pessoas a encontrar o que podem fazer, não fingir que impõem segurança. O frontend é um trilho; o backend é o cadeado.

Construa menus a partir de uma única fonte de verdade

Em vez de espalhar checagens por cada botão, defina sua navegação a partir de uma config que inclua a permissão requerida para cada item, e então renderize a partir dessa config. Isso mantém as regras legíveis e evita checagens esquecidas em cantos estranhos da UI.

Um padrão simples:

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));

Prefira ocultar seções inteiras (como Admin) a espalhar checagens em cada link de admin. São menos pontos para errar.

Estados honestos: oculto vs desativado

Oculte itens quando o usuário nunca terá permissão. Desative itens quando o usuário tem permissão, mas falta contexto.

Exemplo: Delete contact deve estar desativado até que um contato seja selecionado. Mesma permissão, só contexto insuficiente. Quando desativar, adicione uma breve mensagem explicativa (tooltip, texto auxiliar ou nota inline): Selecione um contato para deletar.

Um conjunto de regras que funciona:

  • Oculte quando o usuário não tem permissão.
  • Desative quando o usuário tem permissão, mas nenhum item está selecionado, o formulário é inválido ou dados estão carregando.
  • Nunca trate local storage como verdade para permissões. É só um cache.
  • Rechecagem após login, mudança de papel ou troca de conta.

Aplicação no backend que não pode ser contornada

Ocultar itens ajuda foco, mas não protege nada. O backend precisa ser o juiz final porque requisições podem ser replayed, editadas ou disparadas fora da UI.

Uma boa regra: toda ação que altera dados precisa de uma checagem de autorização, em um único lugar, que toda requisição passe. Pode ser middleware, um wrapper de handler ou uma pequena camada de policy chamada no início de cada endpoint. Escolha uma abordagem e mantenha-a, ou caminhos serão perdidos.

Coloque a checagem no caminho da requisição

Mantenha autorização separada da validação de entrada. Primeiro decida: “este usuário pode fazer isto?”, depois valide o payload. Se validar antes, você pode vazar detalhes (como quais IDs existem) para alguém que nem deveria saber que a ação é possível.

Um padrão escalável:

  • Autentique o chamador e construa uma identidade clara (user id, org id, roles, permissions).
  • Carregue o contexto do recurso necessário para autorização (geralmente owner id ou org id).
  • Chame uma função de policy única (por exemplo: Can(user, "invoice.delete", invoice)).
  • Se negado, pare imediatamente e retorne o status correto.
  • Só então valide e execute a lógica de negócio.

Retorne respostas claras e consistentes

Use códigos de status que ajudem tanto a frontend quanto seus logs:

  • 401 Unauthorized quando o chamador não está logado.
  • 403 Forbidden quando está logado mas não autorizado.

Tenha cuidado com 404 Not Found como disfarce. Pode ser útil para não revelar a existência de um recurso, mas se misturar aleatoriamente complica o debug. Escolha uma regra consistente por tipo de recurso.

Assegure que a mesma autorização rode independente de a ação vir de um clique, app móvel, script ou chamada direta à API.

Finalmente, registre tentativas negadas para debug e auditoria, mas proteja logs. Registre quem, qual ação e qual tipo de recurso em alto nível. Evite campos sensíveis, payloads completos ou segredos.

Casos de borda que fazem ou quebram o design

A maioria dos bugs de permissão aparece quando usuários fazem algo que o menu nunca previu. Por isso menus sensíveis a permissões são úteis, mas só se você projetar também para os caminhos que os contornam.

Se o menu oculta Billing para um papel, um usuário ainda pode colar uma URL salva ou abrir a partir do histórico. Trate cada carregamento de página como uma requisição nova: busque as permissões atuais do usuário e faça a tela recusar carregar dados protegidos quando faltar permissão. Uma mensagem amigável de “Você não tem acesso” é aceitável, mas a proteção real é o backend retornar vazio.

Chamadas diretas à API e endpoints em massa

Qualquer um pode chamar sua API de dev tools, script ou outro cliente. Então cheque permissões em todo endpoint, não só nas telas de admin. O risco fácil de perder são endpoints em massa: um único /items/bulk-update pode permitir que um não-admin altere campos que nunca vê na UI.

Papéis também podem mudar durante a sessão. Se um admin remove uma permissão, o usuário pode ainda ter um token antigo ou estado de menu em cache. Use tokens de curta duração ou lookup server-side das permissões, e trate 401/403 atualizando capacidades e ajustando a UI.

Dispositivos compartilhados criam outro risco: estado do menu em cache pode vazar entre contas. Armazene visibilidade do menu com chave do ID do usuário, ou evite persistir.

Cinco testes que valem a pena antes do release:

  • Abra um deep link para uma página oculta enquanto logado com um papel limitado
  • Chame uma API protegida com uma requisição copiada dos dev tools
  • Mude o papel do usuário enquanto ele está ativo e tente ações novamente
  • Faça logout, login com outro usuário e verifique se o menu foi resetado
  • Teste endpoints em massa com campos mistos permitidos e não permitidos

Cenário de exemplo: um menu CRM simples com aplicação real

Keep your code portable
Export the source code when you want full control over your auth layer.

Imagine um CRM interno com três papéis: Sales, Support e Admin. Todos fazem login e o app mostra um menu à esquerda, mas o menu é só conveniência. A segurança real é o que o servidor permite.

Aqui está um conjunto simples de permissões legível:

  • Leads: view, create, edit, delete, export
  • Tickets: view, create, edit, assign
  • Billing: view, edit
  • Users: view, manage

A UI começa pedindo ao backend as ações que o usuário atual pode fazer (geralmente como lista de strings de permissão) mais contexto básico como user id e time. O menu é construído a partir disso. Se você não tem billing.view, não vê Billing. Se tem leads.export, vê um botão Export na tela de Leads. Se pode editar apenas seus leads, o botão Edit ainda pode aparecer, mas deve ficar desativado ou mostrar uma mensagem clara quando o lead não for seu.

Agora o ponto importante: cada endpoint de ação aplica as mesmas regras.

Exemplo: Sales pode criar leads e editar leads que possui. Support pode ver tickets e atribuir tickets, mas não mexa em billing. Admin pode gerenciar usuários e billing.

Quando alguém tenta deletar um lead, o backend checa:

  1. O usuário tem leads.delete?
  2. Se a regra for “own-only”, lead.owner_id == user.id?
  3. Se o lead estiver bloqueado (por exemplo, já faturado), o delete é bloqueado para todos exceto Admin?

Mesmo se um Support chamar manualmente o endpoint de delete, recebe forbidden. O item oculto no menu nunca foi a proteção. A decisão do backend foi.

Erros comuns e armadilhas

A maior armadilha é achar que o trabalho acabou quando o menu parece certo. Ocultar botões reduz confusão, mas não reduz risco.

Erros mais comuns:

  • “Não visível” vira “impossível”. Alguém ainda pode chamar a API diretamente, usar URL antiga ou disparar a ação via dev tools. Trate ocultação como conveniência, não como trava.
  • Permissões checadas só na UI. Se o frontend decide quem pode deletar, você já perdeu. O backend deve ser o juiz final para cada ação protegida.
  • Um único flag isAdmin para tudo. Parece rápido, depois se espalha. Em pouco tempo cada exceção vira caso especial e ninguém entende as regras.
  • Confiar no cliente para dizer o papel. Nunca aceite role, isAdmin ou permissions vindos do navegador como verdade. Derive identidade e acesso da sua sessão ou token, e busque roles/permissões server-side.
  • Esquecer permissões de leitura. Times focam em escritas, mas vazamentos vêm de leituras. Listagens, visualização de faturas, exports e buscas muitas vezes precisam de checagens também.

Exemplo concreto: você oculta o menu Export leads para não-gerentes. Se o endpoint de export não checar permissões, qualquer usuário que adivinhar a requisição (ou a copiar de um colega) ainda baixa o arquivo.

Checklist rápido pré-lançamento

One source of truth
Generate React menus and a Go API that share the same permission names.

Antes de enviar menus sensíveis a permissões, faça uma passada final focada no que usuários realmente podem fazer, não no que podem ver.

Percorra seu app como cada papel principal e tente o mesmo conjunto de ações. Faça isso na UI e também chamando o endpoint diretamente (ou usando dev tools) para garantir que o servidor é a fonte de verdade.

Checklist:

  • Para cada ação protegida, confirme que há uma checagem server-side no ponto de execução (não apenas ao construir o menu).
  • Faça a UI renderizar ações a partir de capacidades fornecidas pelo servidor (por exemplo, uma can-list da sessão ou endpoint de permissões) em vez de adivinhar pelos nomes de papéis.
  • Verifique se a UI trata respostas proibidas como normais: mostre mensagem clara, mantenha a página estável e evite deixar o usuário em estado quebrado.
  • Teste mudanças de papéis e permissões. Quando um admin atualiza acesso, deve entrar em efeito rapidamente e previsivelmente (refresh de token, invalidação de sessão ou versionamento de permissões).
  • Rode alguns testes de alto valor cobrindo ações que mexem com dinheiro, exports, deletes e configurações admin.

Uma forma prática de achar lacunas: escolha um botão “perigoso” (deletar usuário, exportar CSV, alterar cobrança) e trace-o do início ao fim. O item do menu deve ficar oculto quando apropriado, a API deve rejeitar chamadas não autorizadas e a UI deve se recuperar bem ao receber um 403.

Próximos passos: entregar com segurança sem desacelerar

Comece pequeno. Você não precisa de uma matriz de acesso perfeita no dia 1. Escolha o punhado de ações que mais importam (view, create, edit, delete, export, manage users), mapeie para os papéis que já existem e siga em frente. Quando um novo recurso chegar, adicione só as novas ações que ele introduz.

Antes de construir telas, faça um rápido planejamento listando ações, não páginas. Um item de menu como Invoices esconde muitas ações: ver lista, ver detalhes, criar, reembolsar, exportar. Escrevê-las primeiro deixa UI e regras backend mais claras e evita o erro comum de proteger uma página inteira enquanto deixa um endpoint arriscado desprotegido.

Ao refatorar regras de acesso, trate como qualquer mudança arriscada: mantenha uma rede de segurança. Snapshots permitem comparar comportamento antes e depois. Se um papel perder acesso que precisa, ou ganhar acesso indevido, reverter é mais rápido do que consertar produção enquanto usuários ficam bloqueados.

Uma rotina simples de rollout ajuda times a avançar rápido sem chutar no escuro:

  • Escreva a lista de ações do recurso e nomeie cada permissão.
  • Adicione checagens no backend primeiro, depois conecte a visibilidade da UI às mesmas permissões.
  • Teste um papel permitido e um negado para cada ação de risco.
  • Registre tentativas negadas (sem dados sensíveis) para identificar lacunas.
  • Faça snapshot antes do rollout para poder reverter rápido se necessário.

Se você está construindo com uma plataforma baseada em chat como Koder.ai (koder.ai), a mesma estrutura se aplica: mantenha permissões e policies definidas uma vez, faça a UI ler capacidades do servidor e torne checagens backend não opcionais em cada handler.

Perguntas frequentes

O que os menus sensíveis a permissões realmente resolvem?

Menus sensíveis a permissões resolvem na maior parte das vezes clareza, não segurança. Eles ajudam os usuários a focar no que realmente podem fazer, reduzem cliques que terminam em páginas de erro e cortam perguntas ao suporte como “onde aprovo faturas?”.

A segurança ainda precisa ser aplicada no backend, porque qualquer pessoa pode tentar links diretos, bookmarks antigos ou chamadas diretas à API independentemente do que a interface mostra.

Quando devo ocultar um item do menu vs desativá-lo?

Oculte quando um recurso deve ser praticamente indisponível para um papel e não houver caminho esperado para que esse usuário o utilize.

Desative quando o usuário pode ter acesso, mas falta contexto no momento — por exemplo, nenhum registro selecionado, formulário inválido ou dados ainda carregando. Se desativar, mostre uma explicação curta para não parecer quebrado.

Por que “ocultar o botão” não é segurança?

Porque visibilidade não é autorização. Um usuário pode colar uma URL, reutilizar um bookmark de admin ou chamar sua API fora da UI.

Trate a interface como orientação. Trate o backend como o árbitro final para toda requisição sensível.

Como o frontend deve saber o que o usuário pode fazer?

Seu servidor deve retornar um pequeno payload de “capacidades” depois do login ou ao atualizar a sessão, baseado em checagens de permissão no servidor. A UI então renderiza menus e botões a partir disso.

Não confie em flags vindas do cliente como isAdmin; calcule permissões a partir da identidade autenticada no servidor.

Qual a maneira mais simples de implementar permissões de ponta a ponta?

Comece inventariando ações, não páginas. Para cada recurso separe: ler, criar, atualizar, excluir, exportar, convidar, alterar cobrança, etc.

Depois, aplique cada permissão no handler do backend (ou via middleware/ wrapper) antes de qualquer trabalho. Ligue o menu aos mesmos nomes de permissão para manter UI e API alinhadas.

Devo usar roles ou permissões como modelo principal?

Um padrão prático é: papéis (roles) são agrupamentos, permissões são a fonte de verdade. Mantenha permissões pequenas e orientadas a ações (por exemplo, invoice.create) e anexe-as aos papéis.

Se os papéis começarem a multiplicar para codificar condições (região, leitura apenas), mova essas condições para políticas em vez de criar variações infinitas de papéis.

Como lidar com “pode ver mas não pode editar” e outros acessos condicionais?

Use políticas para regras contextuais como “editar somente seu próprio registro” ou “aprovar faturas abaixo de um limite”. Isso mantém a lista de permissões estável enquanto expressa restrições do mundo real.

O backend deve avaliar a política usando o contexto do recurso (por exemplo, owner ID ou org ID), não suposições da UI.

Preciso mesmo de checagens em endpoints de leitura?

Nem sempre. Leituras que expõem dados sensíveis ou que pulam filtros normais também devem ser restritas, como exports, logs de auditoria, salários ou qualquer endpoint que retorne mais do que a UI normalmente mostra.

Boa regra: todas as escritas precisam ser checadas, e leituras sensíveis também.

Como evitar buracos de segurança com ações em massa e endpoints “potentes”?

Endpoints em massa são fáceis de esquecer porque podem alterar muitos registros ou campos numa única requisição. Verifique permissões para a ação de bulk e valide quais campos aquele papel pode alterar, caso contrário você pode permitir a edição de campos que a UI esconde.

O que acontece quando papéis mudam no meio da sessão ou o cache da UI está desatualizado?

Presuma que permissões podem mudar enquanto alguém está logado. Quando a API retornar 401 ou 403, a UI deve tratar isso como um estado normal: atualizar capacidades, ajustar o menu e mostrar uma mensagem clara.

Também evite persistir estado do menu de forma que possa vazar entre contas em dispositivos compartilhados; se cachear, associe ao ID do usuário ou não persista.

Related posts