Executando verificação de segurança...
10

🗝 Como eu faço as permissões de um usuário em uma API

A etapa de permissões do usuário é muito importante e que na maioria das vezes trazem muitas confusões de como deve ser feita. Abordaremos um proposta usando:

  • Permissões
  • Grupos de permissões

Criando permissões:
Criar uma tabela/array-fixo que terá todas as permissões disponíveis no sistema. Ela será importante para criar os grupos de permissões logo em seguida.

[
    {
      id: "85a738fa-7bf4-4745-93db-01a6e0797035";
      name: "Administrador";
      code: "*";
      options: "*",
      createdAt: "2025-08-22 19:23:01.053"
      updatedAt: "2025-08-22 19:23:01.053"
    },
    {
      id: "85a738fa-7bf4-4745-93db-01a6e0797035";
      name: "Produtos";
      code: "produtos";
      options: ["create", "update", "delete", "viewAll", "viewDetails"],
      createdAt: "2025-08-22 19:23:01.053"
      updatedAt: "2025-08-22 19:23:01.053"
    },
    ...
]

Se no grupo de permissões o usuário tiver o objeto com o code: * significa que esse usuário tem todas as permissões, não necessitando colocar os outros objetos.

Criando grupo de permissões:
Quando criar um usuário no sistema, deve também criar um grupo de usuário para ele. Caso o usuário seja "admin", deve ser criar assim:

{
      id: "85a738fa-7bf4-4745-93db-01a6e0797011";
      name: "Administrator";
      code: "*";
      permissions: [
          {
              id: "85a738fa-7bf4-4745-93db-01a6e0797035";
              name: "Administrador";
              code: "*";
              options: "*",
              createdAt: "2025-08-22 19:23:01.053"
              updatedAt: "2025-08-22 19:23:01.053"
          }
      ],
      userId: "85a738fa-7bf4-4745-93db-01a6e0797123",
      createdAt: "2025-08-22 19:23:01.053"
      updatedAt: "2025-08-22 19:23:01.053"
    }

Observação: Caso você queira economizar memória no banco, pode ser criar apenas um grupo de permissões do tipo administrador, fazendo assim um relacionamento entre o usuário e a tabela admin fixa.

Caso o usuário não seja "admin":

{
      id: "85a738fa-7bf4-4745-93db-01a6e0799090";
      name: "Gerente de produtos";
      code: "gerente-de-produtos";
      permissions: [
          {
              id: "85a738fa-7bf4-4745-93db-01a6e0797035";
              name: "Produtos";
              code: "produtos";
              options: ["create", "update", "delete", "viewAll", "viewDetails"],
              createdAt: "2025-08-22 19:23:01.053"
              updatedAt: "2025-08-22 19:23:01.053"
            },
            {
              id: "85a738fa-7bf4-4745-93db-01a6e0797031";
              name: "Métricas";
              code: "metricas";
              options: ["viewAll", "viewDetails"],
              createdAt: "2025-08-22 19:23:01.053"
              updatedAt: "2025-08-22 19:23:01.053"
            },
            ...
      ],
      userId: "85a738fa-7bf4-4745-93db-01a6e0797009",
      createdAt: "2025-08-22 19:23:01.053"
      updatedAt: "2025-08-22 19:23:01.053"
    }

Observação: O interessante é deixar o usuário "admin" livre para criar quantos grupos de permissões achar necessário.

Frontend:
Ao fazer login no sistema, a API deve mandar o grupo de permissões que o usuário está, para que o frontend não mostre menus, components e telas que o usuário não tem permissão.

Agradecimento...
Espero que tenham gostado desse conteĂşdo ;)
Github: https://github.com/leodeymison

Carregando publicação patrocinada...
4

Muito interessante! Gostei da sua implementação, e com certeza vou implementar em projetos pessoais.

Acredito que para uma melhor segurança a API deveria retornar as rotas/menus a serem renderizado no frontend certo?

Implementamos ao similar em um sistema aqui na empresa, e a API retornava as rotas do usuário.

2

Interessante essa perspectiva. Além de se proteger a API ainda exerce algum controle sobre o frontend
Um ponto a si considerar Ă© o tempo de resposta. Temos que esperar o retorno da API para renderizar algo

1

Sim, além do fato de adicionar uma camada de complexidade. ACHO que vi outros sistemas que usam essa mesma estratégia.

Pelo o que eu me lembro, o tempo de resposta era realmente um problema ;-; Mistura de uma má infra com stack má utilizada (ao meu ver).

1

Essa abordagem de buscar as rotas que irá renderizar no menu é muito interessante. Mas um dúvida que eu tenho é que depois de receber os dados de quais menus pode ser renderizados na rota de buscar menus, esses dados, podem ser capturados e modificados antes de renderizar o frontend? Ou até mesmo invejar um novo array e ir testando quais combinações aparecem novos menus? Pois se uma dessas duas opções funcionarem, fica meio que sem sentido usar processamento da api para fazer isso.

É interessante buscar também as rotas que aquele cliente pode acessar no front, aí já vira meio que uma primeira validação no frontend antes de tentar acessar uma determinada rota.

Em relação a componentes na tela fica meio inviável fazer isso, acredito que até atrapalhe em uma possível manutenção no futuro.

3

Esse post me veio em uma boa hora, muito obrigado! Estava planejando melhorar o sistema de permissionamento de um projeto e essa implementação é muito boa! O melhor é que a mesma abre portas pra atualizações no futuro, então achei bem flexível.

Schema prisma que vou usar:

model User {
  id           String   @id @default(cuid())
  email        String   @unique
  password     String
  name         String
  createdAt    DateTime @default(now())
  updatedAt    DateTime @updatedAt

  // Relação N:N com grupos de permissões
  permissionGroups UserPermissionGroup[]
}

model PermissionGroup {
  id          String   @id @default(cuid())
  name        String   @unique
  description String?
  createdAt   DateTime @default(now())
  updatedAt   DateTime @updatedAt

  // Relação N:N com usuários
  users UserPermissionGroup[]
  // Relação N:N com permissões
  permissions PermissionGroupPermission[]
}

model Permission {
  id          String   @id @default(cuid())
  name        String   @unique // ex: "CREATE_USER", "DELETE_ORDER"
  description String?
  createdAt   DateTime @default(now())
  updatedAt   DateTime @updatedAt

  // Relação N:N com grupos
  groups PermissionGroupPermission[]
}

// Tabela pivot User <-> PermissionGroup
model UserPermissionGroup {
  userId           String
  permissionGroupId String

  user   User           @relation(fields: [userId], references: [id], onDelete: Cascade)
  group  PermissionGroup @relation(fields: [permissionGroupId], references: [id], onDelete: Cascade)

  @@id([userId, permissionGroupId])
}

// Tabela pivot PermissionGroup <-> Permission
model PermissionGroupPermission {
  permissionGroupId String
  permissionId      String

  group      PermissionGroup @relation(fields: [permissionGroupId], references: [id], onDelete: Cascade)
  permission Permission      @relation(fields: [permissionId], references: [id], onDelete: Cascade)

  @@id([permissionGroupId, permissionId])
}