Autenticação e Autorização
Stack de Auth
Seção intitulada “Stack de Auth”- Provider: Supabase Auth (email/password)
- Biblioteca:
@supabase/ssrpara gerenciar sessoes server-side - Cookie de sessão: Gerenciado pelo Supabase SSR automaticamente
- Cookie custom:
x17-active-account-idpara multi-account switching
Clients Supabase
Seção intitulada “Clients Supabase”Browser Client (src/lib/supabase/client.ts)
Seção intitulada “Browser Client (src/lib/supabase/client.ts)”createBrowserClient(SUPABASE_URL, SUPABASE_ANON_KEY)Usado em Client Components. Cria um client com a anon key que respeita RLS.
Server Client (src/lib/supabase/server.ts)
Seção intitulada “Server Client (src/lib/supabase/server.ts)”createServerClient(SUPABASE_URL, SUPABASE_ANON_KEY, { cookies, headers })Usado em API Routes e Server Components. Le/escreve cookies de sessão.
Envia header x-active-account-id quando o cookie x17-active-account-id está presente.
Admin Client (src/lib/supabase/admin.ts)
Seção intitulada “Admin Client (src/lib/supabase/admin.ts)”createClient(SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY, { persistSession: false })Usado para operações que bypassa RLS:
- Webhook processing (não há usuário autenticado)
- Audit logging (fire-and-forget)
- Automation execution (background jobs)
- Cron jobs
Middleware (src/middleware.ts)
Seção intitulada “Middleware (src/middleware.ts)”O middleware do Next.js intercepta TODAS as requests (exceto assets estáticos) e:
-
Aplica security headers em toda response:
X-Content-Type-Options: nosniffX-Frame-Options: DENYX-XSS-Protection: 1; mode=blockReferrer-Policy: strict-origin-when-cross-originPermissions-Policy: camera=(), microphone=(), geolocation=()
-
Skip de rotas públicas (sem sessão Supabase para performance):
/(landing page)/en,/es(landing pages i18n)/privacidade,/termos,/lgpd(páginas legais)/api/whatsapp/webhook(webhook Meta)/api/health(health check)/api/integrations/*/webhook(webhooks e-commerce)/api/cron/*(cron jobs)/t/*(links rastreados)
-
Shopify install redirect: Se
?shop=xxxna raiz, redireciona para/api/integrations/shopify/install -
updateSession(request)para rotas protegidas:- Valida sessão Supabase via
getUser() - Se não logado e acessando
/app/*: redireciona para/login - Se logado e acessando
/loginou/register: redireciona para/app - Atualiza cookies de sessão (refresh token automático)
- Valida sessão Supabase via
Matcher do Middleware
Seção intitulada “Matcher do Middleware”matcher: ["/((?!_next/static|_next/image|favicon.ico|.*\.(?:svg|png|jpg|jpeg|gif|webp)$).*)"]getAuthContext() (src/lib/api/auth.ts)
Seção intitulada “getAuthContext() (src/lib/api/auth.ts)”Função central de autenticação para API Routes. Retorna o contexto do usuário autenticado.
interface AuthContext { userId: string // UUID do usuário Supabase accountId: string // UUID da conta ativa role: string // "owner" | "admin" | "member" | "viewer" | "agent"}Fluxo de resolução:
Seção intitulada “Fluxo de resolução:”- Cria client Supabase server-side
- Chama
supabase.auth.getUser()para validar JWT - Se não autenticado: lança
AuthError(401) - Le cookie
x17-active-account-id:- Se presente: busca
account_userscomuser_id+account_id - Se encontrado: retorna esse account context
- Se não encontrado ou cookie ausente: busca primeiro account do usuário
- Se presente: busca
- Se usuário sem conta: lança
AuthError("Usuário sem conta associada")
Exemplo de uso em API Route:
Seção intitulada “Exemplo de uso em API Route:”export async function GET() { try { const { accountId, userId, role } = await getAuthContext() // ...lógica da rota return success(data) } catch (err) { return error(err) }}requireRole()
Seção intitulada “requireRole()”Função de autorização baseada em role. Lanca AuthError se o role do usuário não está na lista permitida.
requireRole(context, ["owner", "admin"]) // Apenas owner e admin podem acessarHierarquia de Roles (AccountRole)
Seção intitulada “Hierarquia de Roles (AccountRole)”| Role | Permissoes |
|---|---|
| owner | Tudo. Billing, delete account, manage team |
| admin | Tudo exceto billing e delete account |
| member | CRUD de contatos, templates, automações, chat |
| viewer | Somente leitura |
| agent | Chat (atribuido), contatos (leitura) |
Platform Admin
Seção intitulada “Platform Admin”Definido pela env var PLATFORM_ADMIN_EMAILS (lista separada por virgula). Admins da plataforma tem acesso a rota /admin para gerenciar todas as contas.
Fluxo de Login
Seção intitulada “Fluxo de Login”1. Usuário acessa /login2. Frontend envia email/password para Supabase Auth3. Supabase retorna JWT + session cookies4. Middleware detecta usuário logado5. Redireciona para /app6. API Routes usam getAuthContext() para validarFluxo de Account Switching
Seção intitulada “Fluxo de Account Switching”1. Usuário clica em outra conta no dropdown2. Frontend seta cookie x17-active-account-id3. Proxima request: getAuthContext() le o cookie4. Valida que usuário pertence a essa conta via account_users5. Retorna novo AuthContext com o accountId da conta selecionadaTeam Invites
Seção intitulada “Team Invites”Modelo de dados para convite de membros:
interface TeamInvite { id: string account_id: string email: string role: 'admin' | 'member' | 'viewer' invited_by: string token: string status: 'pending' | 'accepted' | 'expired' | 'cancelled' expires_at: string accepted_at: string | null}O fluxo:
- Owner/Admin cria convite com email + role
- Token único gerado
- Convidado recebe link com token
- Ao aceitar: cria registro em
account_userscom o role específicado
Arquivos Relevantes
Seção intitulada “Arquivos Relevantes”| Arquivo | Linha | Descrição |
|---|---|---|
src/middleware.ts | 1-59 | Route protection + security headers |
src/lib/api/auth.ts | 1-58 | getAuthContext() + requireRole() |
src/lib/supabase/client.ts | 1-8 | Browser client |
src/lib/supabase/server.ts | 1-33 | Server client |
src/lib/supabase/admin.ts | 1-14 | Admin/service role client |
src/lib/supabase/middleware.ts | 1-49 | Session validation + redirects |
src/lib/errors.ts | 16-21 | AuthError class |
src/types/database.ts | 3 | AccountRole type |