Este documento descreve o modelo de autenticação, autorização e proteção de dados do ChamadaFácil.
O projeto é um MVP single-company/single-tenant: uma única empresa usa a ferramenta internamente para receber e gerenciar chamados. Ele não é um SaaS multiempresa no escopo atual.
O ChamadaFácil tem dois contextos principais:
| Contexto | Acesso | Descrição |
|---|---|---|
| Público | Sem login | Usuários podem abrir chamados e consultar um chamado específico usando código e e-mail. |
| Administrativo | Com login | Operadores autenticados no Supabase gerenciam chamados, status, urgência e respostas. |
No MVP, qualquer usuário autenticado no Supabase é tratado como operador/admin. Essa é uma decisão consciente para uma ferramenta interna de empresa única.
Condição obrigatória para produção:
- O cadastro público deve ficar desativado no Supabase Auth.
- Admins/operadores devem ser criados manualmente por uma pessoa responsável.
- A lista de usuários em Supabase Auth deve conter apenas pessoas autorizadas.
Essa abordagem não deve ser tratada como autorização completa para um produto maior. Se o sistema crescer, a próxima evolução natural é implementar RBAC e permissões por papel.
A autenticação administrativa usa Supabase Auth com e-mail e senha.
Fluxo atual:
- O operador acessa
/admin/login. - O formulário chama uma Server Action.
- A Server Action usa Supabase Auth para validar e-mail e senha.
- Em caso de sucesso, o usuário é redirecionado para
/admin. - Em caso de falha, a interface mostra uma mensagem genérica de credenciais inválidas.
Arquivos principais:
src/app/(admin)/admin/login/page.tsxsrc/components/admin/admin-login-form.tsxsrc/lib/data/admin-auth-actions.tssrc/lib/supabase/server.ts
As rotas /admin são protegidas em duas camadas:
src/proxy.tsverifica sessão e redireciona usuários não autenticados para/admin/login.- As páginas administrativas também chamam
getAuthenticatedAdminUser()no servidor.
Rotas protegidas:
/admin/admin/tickets/[id]/admin/tickets, que redireciona para/admin
As Server Actions administrativas também revalidam o usuário com supabase.auth.getUser() antes de qualquer mutação. A proteção de página é tratada como controle de navegação e experiência; a autorização real das mutações é conferida novamente no servidor.
Usuários públicos não fazem login e não recebem acesso direto para listar chamados.
Fluxos públicos:
- Criar chamado em
/tickets/new. - Consultar chamado em
/tickets/lookup.
A consulta pública exige:
- número do chamado;
- e-mail usado na abertura.
O resultado retorna apenas o chamado que combina com os dois valores. Não há endpoint público para listar todos os chamados.
RLS está habilitado em:
ticketsticket_responsespublic_rate_limits
No estado final das migrations:
- clientes anônimos não têm grants diretos para selecionar, inserir, atualizar ou deletar tickets;
- criação pública passa por Server Action com validação, same-origin guard e rate limit;
- usuários autenticados podem ler tickets;
- usuários autenticados podem atualizar apenas campos de fluxo necessários ao MVP:
status,is_urgenteresolved_at.
- clientes anônimos não têm acesso direto;
- consulta pública retorna respostas apenas do chamado encontrado por código e e-mail;
- usuários autenticados podem ler e criar respostas públicas.
- usada para controle de abuso em formulários públicos;
- armazena
event_type,subject_hashecreated_at; - não possui policies para acesso anônimo ou autenticado;
- é acessada apenas por código server-side com service role key.
SUPABASE_SERVICE_ROLE_KEY é uma chave sensível e server-only.
Ela é usada no projeto para:
- criar chamado público após validação;
- consultar chamado público após validação;
- registrar e consultar eventos de rate limit.
Regras:
- nunca usar prefixo
NEXT_PUBLIC_nessa variável; - nunca expor em Client Components;
- nunca publicar em screenshots, logs, issues ou documentação pública;
- rotacionar imediatamente se houver suspeita de vazamento.
Obrigatórias:
NEXT_PUBLIC_SUPABASE_URL=
NEXT_PUBLIC_SUPABASE_ANON_KEY=
SUPABASE_SERVICE_ROLE_KEY=| Variável | Exposição | Observação |
|---|---|---|
NEXT_PUBLIC_SUPABASE_URL |
Pública | URL do projeto Supabase. |
NEXT_PUBLIC_SUPABASE_ANON_KEY |
Pública | Chave pública usada com RLS. |
SUPABASE_SERVICE_ROLE_KEY |
Servidor | Chave sensível para fluxos server-side controlados. |
Variáveis com NEXT_PUBLIC_ podem ser incluídas no bundle do navegador. Apenas valores intencionalmente públicos devem usar esse prefixo.
Formulários públicos e administrativos são validados no servidor.
Validações implementadas:
- nome do solicitante entre 2 e 80 caracteres;
- e-mail obrigatório, formato válido e máximo de 160 caracteres;
- telefone opcional com limite e caracteres permitidos;
- assunto entre 5 e 120 caracteres;
- descrição entre 20 e 2000 caracteres;
- número do chamado no formato
CF-YYYY-00000; - status limitado a
open,in_progresseresolved; - resposta do operador entre 2 e 2000 caracteres;
- rejeição de caracteres de controle inseguros.
Arquivos:
src/lib/validation/tickets.tssrc/lib/validation/admin-auth.tssrc/lib/validation/admin-tickets.ts
As ações públicas executam uma verificação de mesma origem antes de processar dados.
Arquivo:
src/lib/security/request-guards.ts
Decisão atual:
- Não há token CSRF customizado.
- Next.js Server Actions já aplicam validação de
Origin/Host. - O projeto adiciona guarda explícita nos fluxos públicos.
- Mutations administrativas revalidam Supabase Auth dentro da Action.
Se o app for colocado atrás de proxy reverso ou domínio customizado, valide os headers de host/origin no ambiente final.
O projeto implementa rate limit básico com a tabela public_rate_limits.
Os sujeitos são armazenados como SHA-256, não como IP/e-mail em texto puro.
Limites atuais:
| Evento | Limite |
|---|---|
| Abertura por IP | 5 tentativas por 15 minutos |
| Abertura por e-mail | 3 tentativas por 1 hora |
| Consulta por IP | 30 tentativas por 10 minutos |
| Consulta por e-mail | 12 tentativas por 10 minutos |
Arquivo:
src/lib/security/rate-limit.ts
Para tráfego real mais alto, adicione uma camada edge/WAF, como Vercel Firewall, Cloudflare WAF ou um rate limiter externo.
next.config.ts configura headers globais:
Content-Security-PolicyX-Frame-OptionsX-Content-Type-OptionsReferrer-PolicyPermissions-Policy
Esses headers reduzem riscos comuns de execução indevida de conteúdo, embedding por terceiros, exposição excessiva de referrer e permissões de navegador desnecessárias.
Antes de publicar:
- Desativar public signups no Supabase Auth.
- Criar admin/operador manualmente no Supabase Dashboard.
- Confirmar que apenas usuários autorizados existem em Auth.
- Configurar variáveis de ambiente no provedor de deploy.
- Aplicar migrations no Supabase remoto.
- Verificar RLS em
tickets,ticket_responsesepublic_rate_limits. - Confirmar que clientes anônimos não listam tickets nem respostas.
- Testar criação pública de chamado.
- Testar consulta correta e consulta com dados errados.
- Testar rate limit com repetição de envios.
- Testar login e logout admin.
- Testar redirecionamento de
/adminsem sessão. - Testar atualização de status, urgência e resposta.
- Garantir que a service role key não aparece no bundle, logs ou screenshots.
- Rodar
npm test. - Rodar
npm run lint. - Rodar
npm run typecheck. - Rodar
npm run build.
- Não há RBAC no MVP.
- Não há permissões por papel.
- Não há auditoria avançada de alterações.
- Não há separação multiempresa.
- Não há CAPTCHA.
- Não há camada WAF configurada no repositório.
- Não há testes de integração/e2e ou testes automatizados de policies RLS no momento.
Essas limitações são aceitáveis para o escopo atual desde que o sistema seja operado como ferramenta interna de uma única empresa e com criação manual de operadores.
- RBAC com papéis
admin,operatoreviewer. - Permissões por ação.
- Auditoria de alteração de status, urgência e respostas.
- Logs defensivos para falhas de login e ações sensíveis.
- Rate limit em edge/WAF.
- CAPTCHA adaptativo para abuso em formulários públicos.
- Políticas multiempresa caso o produto evolua para SaaS.
- Testes automatizados de autorização e políticas RLS.