- Corrigir sintaxe PostgREST em todos serviços (getById, update, delete) * patientService: ?id=eq.uuid * doctorService: ?id=eq.uuid * appointmentService: ?id=eq.uuid * reportService: ?id=eq.uuid - Adicionar componente Chatbot com IA * Respostas inteligentes baseadas em palavras-chave * Quick replies para perguntas frequentes * Integrado em CentralAjuda e CentralAjudaMedico - Padronizar LoginPaciente * Usar loginComEmailSenha (mesma API de LoginSecretaria) * Remover lógica antiga de loginPaciente * Simplificar fluxo de autenticação
MEDICONNECT – Documentação Técnica e de Segurança
Aplicação SPA (React + Vite + TypeScript) consumindo Supabase (Auth, PostgREST, Edge Functions) via Netlify Functions. Este documento consolida: variáveis de ambiente, arquitetura de autenticação, modelo de segurança atual, riscos, controles implementados e próximos passos.
🚀 Guias de Início Rápido
Primeira vez rodando o projeto? Escolha seu guia:
- 📖 QUICK-START.md - Comandos rápidos (5 minutos)
- 📚 README-INSTALACAO.md - Guia completo com troubleshooting
- 🚢 DEPLOY.md - Como fazer deploy no Netlify (produção)
Arquitetura da aplicação:
Frontend (Vite/React) → Netlify Functions → Supabase API
As Netlify Functions protegem as credenciais do Supabase e funcionam como proxy/backend.
⚠️ MUDANÇAS RECENTES NA API (21/10/2025)
Base de Dados Limpa
Todos os usuários, pacientes, laudos e agendamentos foram deletados. Motivo: limpeza de dados inconsistentes e roles incorretos.
Novas Permissões (RLS)
👨⚕️ Médicos:
- ✅ Veem todos os pacientes
- ✅ Veem apenas seus próprios laudos (filtro:
created_by = médico) - ✅ Veem apenas seus próprios agendamentos (filtro:
doctor_id = médico) - ✅ Editam apenas seus próprios laudos e agendamentos
👤 Pacientes:
- ✅ Veem apenas seus próprios dados
- ✅ Veem apenas seus próprios laudos (filtro:
patient_id = paciente) - ✅ Veem apenas seus próprios agendamentos
👩💼 Secretárias:
- ✅ Veem todos os pacientes
- ✅ Veem todos os agendamentos
- ✅ Veem todos os laudos
👑 Admins/Gestores:
- ✅ Acesso completo a tudo
Novos Endpoints de Criação (Atualizado 21/10 - tarde)
⚠️ IMPORTANTE: A API mudou! create-doctor e create-patient (REST) NÃO ENVIAM MAGIC LINK e NÃO CRIAM AUTH USER.
create-user - Criação completa com autenticação (RECOMENDADO):
- Obrigatório:
email,full_name,role - Opcional:
phone,create_patient_record,cpf,phone_mobile - 🔐 Envia magic link automaticamente para ativar conta
- Cria: Auth user + Profile + Role + (opcionalmente) registro em
patients - Use este para criar qualquer usuário que precisa fazer login
create-doctor (Edge Function) - Criação de médico SEM autenticação:
- Obrigatório:
cpf,crm,crm_uf,full_name,email - Validações: CRM (4-7 dígitos), CPF (11 dígitos), UF válido
- ❌ NÃO cria auth user - apenas registro em
doctors - Use apenas se precisar criar registro de médico sem login
POST /rest/v1/patients - Criação de paciente SEM autenticação:
- Obrigatório:
full_name,cpf,email,phone_mobile,created_by - ❌ NÃO cria auth user - apenas registro em
patients - Use apenas se precisar criar registro de paciente sem login
Quando usar cada endpoint:
create-usercomrole="medico": Admin criando médico que precisa fazer logincreate-usercomrole="paciente"+create_patient_record=true: Admin criando paciente com logincreate-usercomrole="admin"/"secretaria": Criar usuários administrativoscreate-doctor: Apenas para registros de médicos sem necessidade de login (raro)POST /rest/v1/patients: Apenas para registros de pacientes sem necessidade de login (raro)
1. Variáveis de Ambiente (.env / .env.local)
| Variável | Obrigatória | Descrição |
|---|---|---|
VITE_SUPABASE_URL |
Sim | URL base do projeto Supabase (https://<ref>.supabase.co) |
VITE_SUPABASE_ANON_KEY |
Sim | Chave pública (anon) usada para Auth password grant e PostgREST |
VITE_APP_ENV |
Não | Identifica ambiente (ex: dev, staging, prod) |
VITE_SERVICE_EMAIL |
Não (desativado) | Email de usuário técnico (não usar em produção no momento) |
VITE_SERVICE_PASSWORD |
Não (desativado) | Senha do usuário técnico (não usar em produção no momento) |
Boas práticas:
- Nunca exponha Service Role Key no frontend.
- Não comitar
.env– usar.env.examplecomo referência.
2. Arquitetura de Autenticação
🔐 Endpoints de Autenticação (Atualizado 21/10/2025)
Login com Email e Senha
- Endpoint:
POST /auth/v1/token?grant_type=password - Netlify Function:
/auth-login - Body:
{ "email": "usuario@exemplo.com", "password": "senha123" } - Resposta:
{ access_token, token_type: "bearer", expires_in: 3600, refresh_token, user: { id, email } } - Uso: Login tradicional com credenciais
Magic Link (Login sem Senha)
- Endpoint:
POST /auth/v1/otp - Netlify Function:
/auth-magic-link - Body:
{ "email": "usuario@exemplo.com" } - Resposta:
200 OK(email enviado) - Uso: Reenviar link de ativação ou login sem senha
- Nota:
create-userjá envia magic link automaticamente na criação
Dados do Usuário Autenticado
- Endpoint:
GET /auth/v1/user - Netlify Function:
/auth-user - Headers:
Authorization: Bearer <access_token> - Resposta:
{ id, email, created_at } - Uso: Verificar sessão atual
Logout
- Endpoint:
POST /auth/v1/logout - Netlify Function:
/auth-logout - Headers:
Authorization: Bearer <access_token> - Resposta:
204 No Content - Uso: Encerrar sessão e invalidar tokens
🔄 Fluxo de Autenticação
- Login: Usuário envia email+senha →
authService.login→POST /auth-login - Tokens: Resposta contém
access_token(curto prazo) +refresh_token(longo prazo) - Interceptor: Anexa
Authorization: Bearer <access_token>+apikeyem todas as requisições - Refresh: Em 401, tenta renovar token automaticamente
- Enriquecimento:
GET /user-infobusca roles, profile e permissions completos
🆕 Criação de Usuário
Edge Function create-user executa:
- Cria auth user
- Cria profile
- Atribui role
- Envia magic link automaticamente
- Opcionalmente cria registro em
patients(secreate_patient_record=true)
🔒 Motivos para Netlify Functions
- Protege
SUPABASE_ANON_KEYno backend - RLS controla acesso por
auth.uid() - Evita exposição de credenciais no frontend
3. Modelo de Autorização & Roles
Roles previstas: admin, gestor, medico, secretaria, paciente, user.
Camadas:
- Supabase Auth: autenticação e identidade (user.id).
- PostgREST + RLS: enforcement de linha/coluna (ex: paciente só vê seus próprios registros; médico vê pacientes atribuídos / futuras policies).
- Edge Functions: operações privilegiadas (criação de usuário composto; agregações que cruzam tabelas sensíveis).
Princípios:
- Menor privilégio: roles específicas são anexadas à tabela
user_roles/ claim custom (via função user-info). - Expansão de permissões sempre via backend controlado (Edge ou admin interface separada).
4. Armazenamento de Tokens
Status revisado: Access Token agora em memória (via tokenStore), Refresh Token em sessionStorage (escopo aba). LocalStorage legado é migrado e limpo.
Motivações da mudança:
- Reduz superfície de ataque para XSS persistente (access token não persiste após reload se atacante injeta script tardio).
- Session scoping limita reutilização indevida do refresh token após fechamento total do navegador.
Persistência atual:
| Tipo | Local | Expiração Natural |
|---|---|---|
| Access Token | Memória JS | exp claim (curto prazo) |
| Refresh Token | sessionStorage | exp claim / revogação backend |
| User Snapshot | Memória JS | Limpo em logout / reload opcional |
Riscos remanescentes:
- XSS ainda pode ler refresh token dentro da mesma aba.
- Ataques supply-chain podem capturar tokens em runtime.
Mitigações planejadas:
- CSP + bloqueio de inline script não autorizado.
- Auditoria de dependências e lockfile imutável.
- (Opcional) Migrar refresh para cookie httpOnly + rotacionamento curto (exige backend/proxy).
Fallback / Migração:
- Em primeira utilização o
tokenStoremigra chaves legacy (authToken,refreshToken,authUser) e remove dolocalStorage.
Operações:
tokenStore.setTokens(access, refresh?)atualiza memória e session.tokenStore.clear()remove tudo (usado em logout e erro crítico de refresh).
Fluxo de Refresh:
- Requisição falha com 401.
- Wrapper (
http.ts) obtém refresh dotokenStore. - Se sucesso, novo par é salvo (access renovado em memória, refresh substituído em session).
- Se falha, limpeza e redirecionamento esperados pelo layer de UI.
Próximos passos (prioridade decrescente):
- Testes e2e validando não persistência pós reload sem refresh.
- Detecção de reuse (se Supabase expor sinalização) e invalidação proativa.
- Adicionar heurística antiflood de refresh (backoff exponencial).
5. Regras de Segurança no Banco (RLS)
Dependemos de Row Level Security para proteger dados. A aplicação pressupõe policies:
- Tabelas de domínio (patients, doctors) filtradas por
auth.uid()(ex: patient.id = auth.uid()). - Tabela de roles apenas legível para o próprio usuário e roles administrativas.
- Operações de escrita restritas ao proprietário ou a roles privilegiadas.
Checklist a validar (fora do front):
[] Policies para SELECT/INSERT/UPDATE/DELETE em cada tabela sensível.
[] Policies específicas para evitar enumerar usuários (ex: profiles).
[] Remoção de permissões públicas redundantes.
6. Edge Functions
Usadas para:
user-info: agrega roles + profile + permissões derivadas.create-user: fluxo atômico de criação (signup + role + domínio) quando disponível.
Critérios para mover lógica para Edge:
- Necessidade de Service Role Key (não pode ir ao front).
- Lógica multi-tabela que exige atomicidade e validação adicional.
- Redução de round-trips (performance e consistência).
7. Decisão: Proxy Backend (A Avaliar)
Status: NÃO implementado.
Quando justificar criar proxy:
| Cenário | Benefício do Proxy |
|---|---|
| Necessidade de Service Role | Segredo fora do client |
| Orquestração complexa >1 função | Transações / consistência |
| Rate limiting custom | Proteção anti-abuso |
| Auditoria centralizada | Logs correlacionados |
Custos de um proxy:
- Latência adicional.
- Manutenção (deploy, uptime, patches de segurança).
- Duplicação parcial de capacidades já cobertas por RLS.
Decisão atual: permanecer sem proxy até surgir necessidade concreta (service role / complexidade). Reavaliar trimestralmente.
8. Hardening do Cliente
Implementado:
- Interceptor único normaliza erros e tenta 1 refresh controlado.
- Remoção de tokens técnicos persistidos.
- Remoção de senha do domínio (ex:
MedicoCreate).
Planejado:
- Content Security Policy estrita (nonce ou hashes para scripts inline).
- Sanitização consistente para HTML dinâmico (não inserir dangerouslySetInnerHTML sem validação).
- Substituir localStorage por memória + fallback volátil.
- Feature Policy / Permissions Policy (desabilitar sensores não usados).
- SRI (Subresource Integrity) para libs CDN (se adotadas no futuro).
9. Logging & Observabilidade
Diretrizes:
- Nunca logar tokens ou refresh tokens.
- Em produção, anonimizar IDs sensíveis onde possível (hash irreversível).
- Separar logs de segurança (auth failures, tentativas repetidas) de logs de aplicação.
Próximo passo: Implementar adaptador de log (console wrapper) com níveis + redaction de padrões (regex para JWT / emails).
10. Tratamento de Erros
Wrapper http fornece shape padronizado ApiResponse<T>.
Princípios:
- Não propagar stack trace de servidor ao usuário final.
- Exibir mensagem genérica em 5xx; detalhada em 4xx previsível (ex: validação).
- Em 401 após falha de refresh -> limpar sessão e redirecionar login.
11. Ameaças Principais & Contramedidas
| Ameaça | Vetor | Contramedida Atual | Próximo Passo |
|---|---|---|---|
| XSS persistente | Input não sanitizado | Sem campos com HTML arbitrário | CSP + sanitização + remover localStorage |
| Token theft | XSS / extensão maliciosa | Sem service role key | Migrar tokens p/ memória |
| Enumeração de usuários | Erros detalhados em login | Mensagem genérica | Rate limit + monitorar padrões |
| Escalada de privilégio | Manipular roles client-side | Roles derivadas no backend (user-info) | Policies de atualização de roles estritas |
| Replay refresh token | Interceptação | TLS + troca de token no refresh | Reduzir lifetime e detectar reuse |
12. Roadmap de Segurança (Prioridade)
- (P1) Migrar tokens para memória + session fallback.
- (P1) Validar/Documentar RLS efetiva para cada tabela.
- (P2) Implementar logging redaction adapter.
- (P2) CSP + lint anti
dangerouslySetInnerHTML. - (P3) Mecanismo de invalidação global de sessão (revogar refresh em logout server-side se necessário).
- (P3) Testes automatizados de rota protegida (e2e smoke).
13. Serviços Atuais (Resumo)
| Domínio | Arquivo | Observações |
|---|---|---|
| Autenticação | authService.ts |
login, logout, refresh, user-info, getCurrentAuthUser |
| Médicos | medicoService.ts |
CRUD + remoção de password do payload |
| Pacientes | pacienteService.ts |
Listagem/CRUD com normalização |
| Roles | userRoleService.ts |
list/assign/delete |
| Criação Usuário | userCreationService.ts |
Edge first fallback manual |
| Relatórios | (planejado) | Pendende confirmar implementação real (reportService.ts) |
| Consultas | (planejado) | Padronizar nome tabela (consultas vs consultations) |
| SMS | smsService.ts |
Placeholder |
Arquivos legados/deprecados destinados a remoção após verificação de ausência de imports: consultaService.ts, relatorioService.ts, listarPacientes.*, pacientes.js, api.js.
14. Convenções de Código
- DB
snake_case-> frontcamelCase. - Limpeza de campos
undefinedantes de mutações (evita null overwrites). - Requisições POST/PUT/PATCH com
Prefer: return=representationquando necessário. - ApiResponse:
{ success: boolean, data?: T, error?: string, message?: string }.
15. Scripts Básicos
Instalação:
pnpm install
Dev:
pnpm dev
Build:
pnpm build
16. Estrutura Simplificada
src/
services/
pages/
components/
entities/
17. Próximos Passos Técnicos (Geral)
- Implementar serviços faltantes (reports/consultas) alinhados ao padrão http wrapper.
- Testes unitários dos mapeadores (medico/paciente) e do fluxo de refresh.
- Avaliar substituição de localStorage (Roadmap P1).
- Revisar necessidade de proxy a cada trimestre (documentar decisão em CHANGELOG/ADR).
18. Desenvolvimento: Tipagem, Validação e Testes
18.1 Geração de Tipos a partir do OpenAPI
Arquivo da especificação parcial: docs/api/openapi.partial.json
Gerar (ou regenerar) os tipos TypeScript:
pnpm gen:api-types
Resultado: src/types/api.d.ts (não editar manualmente). Atualize o spec antes de regenerar.
Fluxo para adicionar/alterar endpoints:
- Editar
openapi.partial.json(paths / schemas). - Rodar
pnpm gen:api-types. - Ajustar services para usar novos tipos (
components["schemas"]["<Nome>"]). - Adicionar/atualizar validação Zod (se aplicável).
- Criar ou atualizar testes.
18.2 Schemas de Validação (Zod)
Arquivo central: src/validation/schemas.ts
Inclui:
loginSchemapatientInputSchema+ mappermapPatientFormToApidoctorCreateSchema/doctorUpdateSchemareportInputSchema+ mappermapReportFormToApi
Boas práticas:
- Validar antes de chamar service.
- Usar mapper para manter isolamento entre modelo de formulário e payload API (snake_case).
- Adicionar novos schemas aqui ou dividir em módulos se crescer (ex:
validation/patient.ts).
18.3 Testes (Vitest)
Config: vitest.config.ts
Scripts:
pnpm test # execução única
pnpm test:watch # modo watch
Suites atuais:
patient.mapping.test.ts: mapeamento form -> APIdoctor.schema.test.ts: normalização de UF, campos obrigatóriosreport.schema.test.ts: payload mínimo e erros
Adicionar novo teste:
- Criar arquivo em
src/tests/*.test.ts. - Importar schema/service a validar.
- Cobrir pelo menos 1 caso feliz e 1 caso de erro.
18.4 Padrões de Services
Cada service deve:
- Usar tipos gerados (
components["schemas"]) para payload/response quando possível. - Encapsular mapeamentos snake_case -> camelCase em funções privadas (ex:
mapReport). - Limpar chaves com valor
undefinedantes de enviar (já adotado em pacientes/relatórios). - Emitir
{ success, data?, error? }uniformemente.
18.5 Endpoints de Arquivos (Foto / Anexos Paciente)
Formalizados na spec com uploads multipart/form-data:
/auth/v1/pacientes/{id}/foto(POST/DELETE)/auth/v1/pacientes/{id}/anexos(GET/POST)/auth/v1/pacientes/{id}/anexos/{anexoId}(DELETE)
Quando backend estabilizar response detalhado (ex: tipos MIME), atualizar schema PacienteAnexo e regenerar tipos.
18.6 Validação de CPF
Endpoint /pacientes/validar-cpf retorna schema ValidacaoCPF:
{
"valido": boolean,
"existe": boolean,
"paciente_id": string | null
}
Integração: usar antes de criar paciente para alertar duplicidade.
18.7 Checklist ao Criar Novo Recurso
- Definir schema no OpenAPI (entrada + saída).
- Gerar tipos (
pnpm gen:api-types). - Criar service com wrappers padronizados.
- Adicionar Zod schema (form/input).
- Criar testes (mínimo: validação + mapeamento).
- Atualizar README (se conceito novo).
- Verificar se precisa RLS/policy nova no backend.
18.8 Futuro: Automação CI
Pipeline desejado:
- Lint → Build → Test → (Gerar tipos e verificar diff do
api.d.ts) → Deploy. - Falhar se
docs/api/openapi.partial.jsonmudou semapi.d.tsregenerado.
19. Referência Rápida
| Ação | Comando |
|---|---|
| Instalar deps | pnpm install |
| Dev server | pnpm dev |
| Build | pnpm build |
| Gerar tipos API | pnpm gen:api-types |
| Rodar testes | pnpm test |
| Testes em watch | pnpm test:watch |
| Atualizar spec + tipos | editar spec → pnpm gen:api-types |
19.1 Acessibilidade (A11y)
Recursos implementados para melhorar usabilidade, leitura e inclusão:
Preferências do Usuário
Gerenciadas via hook useAccessibilityPrefs (localStorage, chave única accessibility-prefs). As opções persistem entre sessões e são aplicadas ao elemento <html> como classes utilitárias.
| Preferência | Chave interna | Classe aplicada | Efeito Principal |
|---|---|---|---|
| Tamanho da Fonte | fontSize |
(inline style root) | Escala tipográfica global |
| Modo Escuro | darkMode |
dark |
Ativa tema dark Tailwind |
| Alto Contraste | highContrast |
high-contrast |
Contraste forte (cores simplificadas) |
| Fonte Disléxica | dyslexicFont |
dyslexic-font |
Aplica fonte OpenDyslexic (fallback legível) |
| Espaçamento Linhas | lineSpacing |
line-spacing |
Aumenta line-height em blocos de texto |
| Reduzir Movimento | reducedMotion |
reduced-motion |
Remove / suaviza animações não essenciais |
| Filtro Luz Azul | lowBlueLight |
low-blue-light |
Tonalidade quente para conforto visual noturno |
| Modo Foco | focusMode |
focus-mode |
Atenua elementos fora de foco (leitura seletiva) |
| Leitura de Texto | textToSpeech |
(sem classe) | TTS por hover (limite 180 chars) |
Atalho de teclado: Alt + A abre/fecha o menu de acessibilidade. Esc fecha quando aberto.
Componente AccessibilityMenu
- Dialog semântico com
role="dialog",aria-modal="true", foco inicial e trap de tab. - Botões toggle com
aria-pressede feedback textual auxiliar. - Reset central limpa preferências e cancela síntese de fala ativa.
Formulários
- Todos os campos críticos com
id+labelassociada. - Atributos
autoCompletecoerentes (ex:email,name,postal-code,bday,new-password). - Padrões (
pattern) einputModepara CPF, CEP, telefone, DDD, números. aria-invalid+ mensagens condicionais (ex: confirmação de senha divergente).- Normalização para envio (CPF/telefone/cep) realizada no service antes do request (sem formatação).
Tabela de Pacientes
- Usa
scope="col"nos cabeçalhos, suporte dark mode, indicador VIP comaria-label.
Temas & CSS
Classes utilitárias adicionadas em index.css permitindo expansão futura sem alterar componentes. O design evita uso de inline style exceto na escala de fonte global, facilitando auditoria e CSP.
Boas Práticas Futuras
- Adicionar detecção automática de
prefers-reduced-motionpara estado inicial. - Implementar fallback de TTS selecionável por foco + tecla (reduzir leitura acidental).
- Testes automatizados de acessibilidade (axe-core) e verificação de contraste.
- Suporte a aumentar espaçamento de letras (letter-spacing) opcional.
19.2 Testes de Acessibilidade & Fallback de Render (Status Temporário)
Resumo do Problema:
Durante a criação de testes de interface para o AccessibilityMenu, o ambiente de testes (Vitest + jsdom e também happy-dom) deixou de materializar a árvore DOM de componentes React – inclusive para um componente mínimo (<div>Hello</div>). Não houve erros de compilação nem warnings relevantes, apenas container.innerHTML === '' após render(...).
Hipóteses já investigadas (sem sucesso):
- Troca de
@vitejs/plugin-react-swcpor@vitejs/plugin-react(padrão Babel) + pin de versão do Vite (5.4.10). - Alternância de ambiente (
jsdom->happy-dom). - Remoção/isolamento de ícones (
lucide-react) e libs auxiliares (mock de@axe-core/react). - Render manual via
createRoote flush de microtasks. - Ajustes de transform / esbuild jsx automatic.
Decisão Temporária (para garantir “teste que funciona”):
- Marcar suites unitárias dependentes de render React como
describe.skipenquanto a causa raiz é isolada. - Introduzir um teste E2E real em browser (Puppeteer) que valida a funcionalidade essencial do menu.
Arquivos Impactados:
- Skipped (com TODO):
src/__tests__/accessibilityMenu.semantic.test.tsxsrc/__tests__/miniRender.test.tsxsrc/__tests__/manualRootRender.test.tsx
- Novo teste E2E:
src/__tests__/accessibilityMenu.e2e.test.ts
Script E2E:
pnpm test:e2e-menu
O teste:
- Sobe (ou reutiliza) o dev server Vite (porta 5173).
- Abre a SPA no Chromium headless.
- Clica no botão do menu de acessibilidade.
- Verifica presença do diálogo (role="dialog") e depois fecha.
Critério de Aceite Provisório: Enquanto o bug de render unitário persistir, a cobertura de comportamento crítico do menu é garantida pelo teste E2E (abre, foca, fecha). As preferências de acessibilidade continuam cobertas por testes unitários puros (sem render React) onde aplicável.
Próximos Passos para Retomar Testes Unitários:
- Criar reprodutor mínimo externo (novo repo) com dependências congeladas para confirmar se é interação específica local.
- Rodar
pnpm ls --depth 0e comparar versões dereact,react-dom,@types/react,vitest,@vitejs/plugin-react. - Forçar transpile isolado de um teste (
vitest --run --no-threads --dom) para descartar interferência de thread pool. - Se persistir, habilitar logs detalhados de Vite (
DEBUG=vite:*) e inspecionar saída transformada de um teste simples. - Reintroduzir gradativamente (mini -> menu) removendo mocks temporários.
Quando Corrigir:
- Remover skips (
describe.skip). - Reativar (opcional) auditoria
axe-corecom@axe-core/react. - Documentar causa raiz aqui (ex: conflito de plugin, polyfill global, etc.).
Risco Residual: Falhas específicas de acessibilidade sem cobertura E2E mais profunda (ex: foco cíclico em condições de teclado complexas) podem passar. Mitigação: expandir cenários E2E após estabilizar ambiente unitário.
Estado Atual: Fallback E2E ativo e validado. (Atualizar este bloco quando o pipeline unitário React estiver normalizado.)
18. ADRs (Decisões Arquiteturais) Resumidas
| ID | Decisão | Status | Justificativa |
|---|---|---|---|
| ADR-001 | Sem proxy backend inicial | Ativo | RLS + Edge Functions suficientes agora |
| ADR-002 | Tokens em memória + refresh em sessionStorage | Ativo | Redução de risco XSS mantendo simplicidade |
| ADR-003 | Criação de usuário via Edge fallback manual | Ativo | Resiliência caso função indisponível |
Registrar novas decisões futuras em uma pasta docs/adr.
19. Checklist de Release (Segurança)
[] Remover credenciais de desenvolvimento do README / código. [] Validar CSP ativa no ambiente (report-only -> enforce). [] Executar análise de dependências (npm audit / pnpm audit) e corrigir críticas. [] Verificar que nenhum token aparece em logs. [] Confirmar policies RLS completas.
20. Notas Finais
Este documento substitui versões anteriores e consolida segurança + operação. Atualize sempre que fluxos críticos mudarem (auth, roles, storage de tokens, Edge Functions novas).
Última atualização: (manter manualmente) 2025-10-03.
21. Logging Centralizado & Redaction
Implementado logger.ts substituindo gradualmente console.*.
Características:
- Níveis: debug, info, warn, error.
- Redação automática de:
- Padrões de JWT (três segmentos base64url).
- Campos com
token,password,secret,email. - Emails em strings.
- Nível dinâmico: produção =>
info+, demais =>debug.
Uso:
import { logger } from 'src/services/logger';
logger.info('login success', { userId });
Práticas recomendadas:
- Não logar payloads completos com PII.
- Remover valores sensíveis antes de enviar para meta.
- Usar
errorsomente para falhas não recuperáveis ou que exigem telemetria.
Backlog de logging:
- Adicionar transporte opcional (Sentry / Logtail).
- Exportar métricas (Prometheus / OTEL) para 401s e latência.
Status adicional:
- Mascaramento de CPF implementado (
***CPF***XX). - Contador global de 401 consecutivos com limite (3) antes de limpeza forçada de sessão.
22. Política CSP (Rascunho)
Objetivo: mitigar XSS e exfiltração de contexto.
Cabeçalho sugerido (Report-Only inicial):
Content-Security-Policy-Report-Only: \
default-src 'self'; \
script-src 'self' 'strict-dynamic' 'nonce-<nonce-value>' 'unsafe-inline'; \
style-src 'self' 'unsafe-inline'; \
img-src 'self' data: blob:; \
font-src 'self'; \
connect-src 'self' https://*.supabase.co; \
frame-ancestors 'none'; \
base-uri 'self'; \
form-action 'self'; \
object-src 'none'; \
upgrade-insecure-requests; \
report-uri https://example.com/csp-report
Adoção:
- Aplicar em modo report-only (Netlify / edge) e coletar violações.
- Eliminar dependências inline e remover
'unsafe-inline'. - Adicionar hashes/nonce definitivos.
- Migrar para modo enforce.
Complementos:
- Lint contra
dangerouslySetInnerHTMLsem sanitização. - Biblioteca de sanitização (ex: DOMPurify) caso HTML dinâmico seja necessário.
23. Contador de 401 Consecutivos
Mecânica:
- Cada resposta final 401 (sem refresh bem-sucedido) incrementa contador global.
- Sucesso de requisição ou refresh resetam o contador.
- Ao atingir 3, sessão é limpa (
tokenStore.clear()) e próximo acesso exigirá novo login.
Racional: evitar loops silenciosos de requisições falhando e reduzir superfície de brute force de refresh.
Parâmetros:
- Limite atual: 3 (configurável em
src/services/authConfig.ts).
24. Verificação de Drift do OpenAPI
Script: pnpm check:api-drift
Fluxo CI recomendado:
- Rodar
pnpm check:api-drift. - Se falhar, forçar desenvolvedor a executar
pnpm gen:api-typese commitar.
Implementação: gera tipos em memória via openapi-typescript e compara com src/types/api.d.ts normalizando quebras de linha.
25. Mascaramento de CPF no Logger
Padrão suportado: 11 dígitos com ou sem formatação (000.000.000-00).
Saída: ***CPF***00 (mantendo apenas os dois últimos dígitos para correlação mínima).
Objetivo: evitar exposição de identificador completo em logs persistentes.