RBAC, ou Role-Based Access Control, organiza permissões por papéis como administrador, suporte, editor e usuário. Em aplicações Node.js, ele reduz verificações espalhadas e cria uma política central de autorização. O modelo precisa aplicar menor privilégio, negar por padrão e considerar o recurso acessado, não apenas a rota.
Autenticação e autorização
Autenticação responde quem é o usuário. Autorização decide o que ele pode fazer. Um JWT válido ou sessão ativa não concede automaticamente acesso administrativo.
Modelo simples
const rolePermissions = {
admin: new Set(['users:read', 'users:write', 'billing:read']),
support: new Set(['users:read']),
editor: new Set(['posts:read', 'posts:write']),
user: new Set(['profile:read', 'profile:write']),
};
function can(role, permission) {
return rolePermissions[role]?.has(permission) ?? false;
}Use nomes estáveis de permissão. Evite regras como isAdmin espalhadas pelo código.
Middleware
function requirePermission(permission) {
return (req, res, next) => {
if (!req.auth || !can(req.auth.role, permission)) {
res.status(403).json({ error: 'forbidden' });
return;
}
next();
};
}
app.get('/users', requireJwt, requirePermission('users:read'), listUsers);Negar por padrão
Uma rota nova não deve ficar acessível porque ninguém adicionou uma regra. Crie convenções e testes que falhem quando endpoints sensíveis não possuem middleware de autorização.
Papéis no banco
CREATE TABLE roles (
id UUID PRIMARY KEY,
name TEXT UNIQUE NOT NULL
);
CREATE TABLE role_permissions (
role_id UUID NOT NULL,
permission TEXT NOT NULL,
PRIMARY KEY (role_id, permission)
);Em sistemas simples, configuração em código é mais previsível. Em sistemas multi-tenant ou administráveis, banco pode ser necessário.
Usuário com múltiplos papéis
function permissionsForRoles(roles) {
const permissions = new Set();
for (const role of roles) {
for (const permission of rolePermissions[role] || []) {
permissions.add(permission);
}
}
return permissions;
}Defina se permissões são apenas aditivas ou se existe negação explícita. Misturar os dois modelos sem precedência clara cria erros.
Escopo do recurso
RBAC puro pode dizer que um editor pode alterar posts, mas ainda é preciso decidir quais posts. Verifique propriedade, tenant, estado e outras condições:
if (post.tenantId !== req.auth.tenantId) {
throw new ForbiddenError();
}
if (!can(req.auth.role, 'posts:write')) {
throw new ForbiddenError();
}Multi-tenant
Um usuário pode ser admin em um tenant e leitor em outro. Não coloque apenas role=admin no token sem contexto. Modele associação:
userId + tenantId + roleValide tenant pelo recurso e não apenas por header fornecido pelo cliente.
Hierarquia de papéis
Hierarquia como admin > editor > viewer parece simples, mas papéis reais nem sempre são lineares. Permissões explícitas são mais fáceis de auditar que números de nível.
Claims no JWT
Roles no JWT ficam desatualizadas até a expiração. Use access tokens curtos e, para operações críticas, consulte a autorização atual ou uma versão de permissões.
Versão de autorização
if (payload.authzVersion !== user.authzVersion) {
throw new AuthenticationError('Token desatualizado');
}Incrementar a versão permite invalidar tokens após alteração importante.
Separação de funções
Algumas operações exigem que quem cria não aprove. Modele permissões distintas e registre o autor da ação. RBAC pode implementar segregação, mas a regra precisa estar no domínio.
Elevação temporária
Acesso administrativo temporário deve expirar automaticamente, exigir MFA e gerar auditoria. Não altere um usuário permanentemente para resolver um incidente curto.
Contas de serviço
Serviços não deveriam receber papel humano genérico. Use identidades e permissões específicas, como invoice-worker com invoices:process.
UI não é controle
Ocultar botão melhora experiência, mas o backend precisa negar a requisição. Clientes podem chamar endpoints diretamente.
Resposta 401 ou 403
- 401: identidade ausente ou inválida;
- 403: identidade válida sem permissão.
Para recursos sensíveis, às vezes 404 reduz enumeração, mas a política deve ser consistente.
Cache de permissões
Cache reduz consultas, mas precisa de TTL curto ou invalidação. Uma revogação urgente não pode levar horas para surtir efeito.
Auditoria
Registre mudanças de papel, concessões, revogações e uso de ações críticas. Inclua ator, alvo, tenant, permissão, timestamp e request ID, sem dados sensíveis.
Testes de matriz
const cases = [
['admin', 'users:write', true],
['support', 'users:write', false],
['support', 'users:read', true],
];
for (const [role, permission, expected] of cases) {
expect(can(role, permission)).toBe(expected);
}Teste também isolamento entre tenants, tokens desatualizados e propriedade do recurso.
Erros comuns
- confiar na interface;
- role admin sem tenant;
- permissões em strings espalhadas;
- não negar por padrão;
- tokens longos com role antiga;
- cache sem invalidação;
- papéis excessivamente amplos;
- não auditar mudanças;
- confundir papel com propriedade do recurso.
Fluxo recomendado
- liste ações do domínio;
- crie permissões estáveis;
- agrupe em papéis;
- negue por padrão;
- valide tenant e recurso;
- use tokens curtos;
- audite alterações;
- teste a matriz;
- revise menor privilégio.
Combine RBAC com JWT no Node.js, API Keys, Pino e Secret Management.
Consulte o guia de autorização da OWASP e a referência de RBAC do NIST.


