Pular para o conteúdo
Todos os artigos

Isolamento SaaS multi-tenant com Postgres RLS e autenticação de workspace

Combina verificações de pertença ao workspace em tempo real no servidor com políticas indexadas de Postgres Row-Level Security para manter os dados de cada tenant isolados na web, no mobile e em tarefas em segundo plano.

a equipa supastack2 min de leitura

Should I use Postgres Row-Level Security or application-level workspace checks?

It depends on how your application connects to Postgres. If browser or mobile clients query the database directly through PostgREST, Row-Level Security (RLS) is mandatory. If all traffic flows through a server-side TypeScript boundary, explicit membership checks on every query are your primary defense, and RLS serves as defense-in-depth against forgotten WHERE clauses. Combine live session-to-membership resolution with indexed workspace_id policies. supastack enforces server-side workspace guards across web, mobile, and desktop, and provides a free RLS policy generator for Supabase tables.

Guardas da aplicação e políticas da base de dados resolvem falhas diferentes

Os programadores tratam muitas vezes a autorização ao nível da aplicação e a Row-Level Security do Postgres como mutuamente exclusivas. Na prática, intercetam bugs diferentes: as guardas no servidor aplicam hierarquia de papéis, verificações de direitos e registos de auditoria antes da lógica de negócio correr, enquanto o RLS garante que uma cláusula WHERE workspace_id = ... esquecida nunca expõe as linhas de todos os tenants.

Quer uses Supabase ou Postgres autoalojado com Drizzle, todas as tabelas pertencentes a um tenant devem ter uma chave estrangeira workspace_id não nula. O artigo sobre fronteiras do workspace explica porque é que esse âmbito deve ser sempre derivado no servidor.

  • As guardas da aplicação validam a validade da sessão, os papéis dos membros (owner, admin, member) e os direitos de funcionalidades.
  • As políticas RLS da base de dados funcionam como uma barreira final quando são usadas queries diretas do cliente ou helpers de SQL bruto.
  • Ambas as camadas dependem de uma única fonte de verdade: a tabela de junção workspace_members.

Resolve a pertença em tempo real em vez de confiar em cookies desatualizados

Guardar um currentWorkspaceId num cookie do navegador ou cabeçalho do cliente é conveniente para a navegação, mas um cookie é uma preferência, não uma autorização. Se um administrador remover um colega de um workspace, o navegador desse utilizador continua a guardar o cookie antigo.

Cruza a sessão ativa com workspace_members em cada pedido. Quando a linha de pertença já não existir, rejeita o pedido de imediato em vez de esperar que um JWT ou cookie de sessão expire. O artigo sobre autenticação além do formulário de login mostra como a identidade alimenta esta verificação.

export async function requireWorkspaceViewer(requestedWorkspaceId?: string) {
  const session = await requireAuthenticatedSession();
  const membership = await db.query.workspaceMembers.findFirst({
    where: and(
      eq(workspaceMembers.userId, session.user.id),
      requestedWorkspaceId
        ? eq(workspaceMembers.workspaceId, requestedWorkspaceId)
        : undefined,
    ),
    with: { workspace: true },
  });

  if (!membership) throw new ForbiddenError("Not a member of this workspace");
  return { user: session.user, workspace: membership.workspace, role: membership.role };
}

Escreve políticas de Row-Level Security rápidas e indexadas

Políticas RLS ingénuas que chamam auth.uid() diretamente em cada linha obrigam o Postgres a reavaliar a função por cada registo. Envolver (select auth.uid()) numa subquery escalar permite ao planeador colocar o valor em cache como um initPlan, e um índice composto em (workspace_id, user_id) mantém as pesquisas de pertença abaixo do milissegundo.

Podes gerar políticas SELECT, INSERT, UPDATE e DELETE prontas a executar para tabelas multi-tenant usando o gerador gratuito de políticas RLS para Supabase sem escrever SQL repetitivo à mão.

CREATE INDEX IF NOT EXISTS idx_workspace_members_lookup
  ON workspace_members (workspace_id, user_id);

ALTER TABLE documents ENABLE ROW LEVEL SECURITY;

CREATE POLICY "workspace_members_manage_documents"
ON documents FOR ALL TO authenticated
USING (
  workspace_id IN (
    SELECT wm.workspace_id FROM workspace_members wm
    WHERE wm.user_id = (SELECT auth.uid())
  )
)
WITH CHECK (
  workspace_id IN (
    SELECT wm.workspace_id FROM workspace_members wm
    WHERE wm.user_id = (SELECT auth.uid())
      AND wm.role IN ('owner', 'admin', 'member')
  )
);

Perguntas frequentes sobre RLS e autenticação de workspace

As equipas que configuram Postgres multi-tenant encontram os mesmos casos limite em torno de service roles, tarefas em segundo plano e desempenho.

  • O Drizzle ORM ou o Prisma contornam o RLS? — Sim, se o teu servidor se ligar com o superutilizador postgres ou com a chave service_role do Supabase. É por isso que as server actions e rotas de API devem chamar requireWorkspaceViewer() e filtrar cada query por workspaceId.
  • Porquê envolver (select auth.uid()) entre parênteses nas políticas RLS? — Indica ao otimizador do Postgres para avaliar auth.uid() uma única vez por instrução em vez de uma vez por linha analisada, evitando sequential scans lentos em tabelas grandes.
  • Como devem as tarefas em segundo plano aceder aos dados do tenant? — Passa o workspaceId validado no payload do job a partir da rota autenticada do servidor e limita as leituras e escritas do worker a esse workspaceId.
  • Como verifico que o isolamento entre tenants funciona mesmo? — Escreve testes negativos automatizados em que o Utilizador A pede ou altera o ID de registo do Utilizador B através de rotas web, mobile e API, e confirma que devolvem 403 / 404 com zero linhas alteradas.

Perguntas frequentes

14 perguntas

O que é o supastack?

O supastack é uma base modular para construir produtos SaaS com IA. Reúne a infraestrutura comum para que possas concentrar-te nas funcionalidades próprias do teu produto.

Suporta web e mobile?

Sim. O supastack inclui apps Next.js e Expo como pontos de partida para experiências web e mobile.

Que autenticação está incluída?

O Better Auth cobre email/palavra-passe, OTP e login social. Os workspaces pessoais são aprovisionados com a conta para que os registos fiquem delimitados desde o primeiro dia.

Como é suportada a IA?

O supastack inclui IA multi-fornecedor configurável, registo de utilização, orçamentos e suporte encriptado de chaves próprias (BYOK).

Como são geridas as subscrições?

Para produtos construídos sobre o kit, a faturação Stripe, as subscrições e as permissões formam a base do acesso pago. Os segredos de fornecedor ficam no servidor.

Onde vive o código próprio do meu produto?

As funcionalidades próprias do produto ficam num codebase separado, enquanto o produto configura e integra os módulos base do supastack.

Como funciona o preço do supastack?

O kit é uma compra única com atualizações vitalícias: Stack único por 249 $ — à escolha entre os repositórios Next.js (web), Expo (iOS e Android) ou Tauri (macOS e Windows) —, Stack duplo por 398 $ com dois repositórios à escolha, ou Pacote completo por 399 $ com os três repositórios e o monorepo completo, mais 75 $ por lugar de programador adicional. Sem subscrição do kit. O checkout neste site entrega a tua chave de licença por email, com acesso ao repo por convite GitHub.

Como é que os workspaces delimitam os meus dados?

Os registos do utilizador são delimitados por workspaceId. Identidade, permissões, utilização de IA, eventos de faturação e entradas de auditoria ficam dentro do limite do workspace pessoal.

Como funcionam os jobs em segundo plano?

O Trigger.dev é o runtime de jobs explícito. O contrato JobRunner só promete enqueue, status e cancel — a semântica de workflow permanece específica e explícita do Trigger.dev.

Como difere o email da divulgação?

O email transacional cobre fluxos de produto como avisos de conclusão de tarefas. A divulgação fria e as campanhas pertencem ao próprio produto, fora do módulo de email deste kit.

Como funcionam as compras no aplicativo móvel?

O aplicativo móvel inclui compras nativas e restauração via RevenueCat, com acesso verificado pelo servidor. Configure as contas das lojas e conclua os testes em dispositivos antes de publicar. A cobrança web permanece separada.

Onde residem as chaves dos fornecedores de IA?

As credenciais BYOK são encriptadas e ficam no servidor. As chaves são higienizadas nos logs; a utilização e os orçamentos ficam visíveis ao nível do workspace.

Quais métodos de pagamento o checkout aceita?

O checkout hospedado na Stripe aceita cartões e também pode oferecer Alipay e WeChat Pay quando essas carteiras estiverem ativadas na conta Stripe — útil para compradores na China. As autorizações de carteira são confirmadas de forma assíncrona, então a página da licença é atualizada assim que o pagamento é liquidado.

Posso solicitar um reembolso?

Depois de obter acesso ao repositório, o supastack é seu para sempre, portanto não pode ser reembolsado. Mas fique tranquilo: você recebe o código-fonte completo com atualizações vitalícias para criar e lançar produtos ilimitados para você ou seus clientes. Você apenas não pode revender o código nem redistribuir o repositório como starter kit ou modelo. Uma exceção é o cashback de lançamento — lance um produto real feito com supastack, compartilhe-o e alcance 5.000 visualizações orgânicas ou 50 curtidas orgânicas em até 60 dias para receber de volta 50% do valor pago; consulte os termos.

PT