Como Garantir Acesso Seguro aos Dados? Dúvida sobre Abordagem Multi-Tenant.
Já faz um tempinho que compartilhei aqui uma aplicação que estava desenvolvendo na empresa. Até então, ela era usada só internamente, por isso optei por manter um único banco de dados com todas as informações. Mas, ao pensar em expandir isso para outras empresas e adotar uma abordagem multi-tenant, me peguei com uma dúvida: Como garantir que só os usuários de uma empresa tenham acesso aos dados dela?
Pensei em incluir os IDs do usuário e da empresa nos cabeçalhos das requisições, considerando que ele ja tenha uma sessão autenticada, deixando para a API decidir quais dados devem ser entregues.
A ideia é simples: primeiro, a API confirma se o usuário realmente pertence à empresa; depois, filtra e entrega só os contratos relacionados à empresa desse usuário.
Queria saber se tem algum problema nesse processo ou se há uma forma melhor de fazer isso.
Não liguem pro hard-code, tratamento de erros, etc... isso é só uma POC...
import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
function getTenantFromRequest(context) {
const { usuario_id, empresa_id } = context.token
return { usuario_id, empresa_id }
}
const typeDefs = `#graphql
type Query {
contratos: [Contrato]
}
type Contrato {
id: ID
descricao: String
empresa_id: String
}
`;
const contratos = [
{ id: '1', descricao: 'Rio Novo do Sul', empresa_id: '1' },
{ id: '2', descricao: 'Venda Nova', empresa_id: '2' },
{ id: '3', descricao: 'Vitoria', empresa_id: '2' },
];
const empresas = [
{ id: '1', nome: 'Volt', contratos_id: ['1'], usuarios_id: ['1', '2'] },
{ id: '2', nome: 'EletroMarquez', contratos_id: ['2', '3'], usuarios_id: ['3'] },
];
const usuarios = [
{ id: '1', nome: 'Elias', empresa_id: '1' },
{ id: '2', nome: 'Pedro', empresa_id: '2' }
];
const resolvers = {
Query: {
contratos: (parent, args, context) => {
const { empresa_id, usuario_id } = getTenantFromRequest(context)
const empresa = empresas.find((empresa) => empresa.id === empresa_id);
if (!empresa) {
throw new Error('Contrato não encontrado');
}
const usuarioEstaNaEmpresa = empresa.usuarios_id.includes(usuario_id);
if (usuarioEstaNaEmpresa) {
const contratosDoInquilino = contratos.filter(contrato => contrato.empresa_id === empresa_id);
return contratosDoInquilino;
} else {
throw new Error('Contrato não encontrado');
}
}
}
};
const server = new ApolloServer({ typeDefs, resolvers });
const { url } = await startStandaloneServer(server, {
context: async ({ req }) => ({ token: req.headers }),
listen: { port: 4000 },
});
console.log(`🚀 Server ready at: ${url}`);
Até onde eu entendi não há problema nenhum, é uma pratica bem comum do mercado colocar informações que são usadas em filtros no contexto do usuario, ex: area do usuário, empresa do usuario e até cargo.