Pular para o conteúdo

Autenticação e Autorização

  • Provider: Supabase Auth (email/password)
  • Biblioteca: @supabase/ssr para gerenciar sessoes server-side
  • Cookie de sessão: Gerenciado pelo Supabase SSR automaticamente
  • Cookie custom: x17-active-account-id para multi-account switching
createBrowserClient(SUPABASE_URL, SUPABASE_ANON_KEY)

Usado em Client Components. Cria um client com a anon key que respeita RLS.

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.

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

O middleware do Next.js intercepta TODAS as requests (exceto assets estáticos) e:

  1. Aplica security headers em toda response:

    • X-Content-Type-Options: nosniff
    • X-Frame-Options: DENY
    • X-XSS-Protection: 1; mode=block
    • Referrer-Policy: strict-origin-when-cross-origin
    • Permissions-Policy: camera=(), microphone=(), geolocation=()
  2. 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)
  3. Shopify install redirect: Se ?shop=xxx na raiz, redireciona para /api/integrations/shopify/install

  4. 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 /login ou /register: redireciona para /app
    • Atualiza cookies de sessão (refresh token automático)
matcher: ["/((?!_next/static|_next/image|favicon.ico|.*\.(?:svg|png|jpg|jpeg|gif|webp)$).*)"]

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"
}
  1. Cria client Supabase server-side
  2. Chama supabase.auth.getUser() para validar JWT
  3. Se não autenticado: lança AuthError (401)
  4. Le cookie x17-active-account-id:
    • Se presente: busca account_users com user_id + account_id
    • Se encontrado: retorna esse account context
    • Se não encontrado ou cookie ausente: busca primeiro account do usuário
  5. Se usuário sem conta: lança AuthError("Usuário sem conta associada")
export async function GET() {
try {
const { accountId, userId, role } = await getAuthContext()
// ...lógica da rota
return success(data)
} catch (err) {
return error(err)
}
}

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 acessar
RolePermissoes
ownerTudo. Billing, delete account, manage team
adminTudo exceto billing e delete account
memberCRUD de contatos, templates, automações, chat
viewerSomente leitura
agentChat (atribuido), contatos (leitura)

Definido pela env var PLATFORM_ADMIN_EMAILS (lista separada por virgula). Admins da plataforma tem acesso a rota /admin para gerenciar todas as contas.

1. Usuário acessa /login
2. Frontend envia email/password para Supabase Auth
3. Supabase retorna JWT + session cookies
4. Middleware detecta usuário logado
5. Redireciona para /app
6. API Routes usam getAuthContext() para validar
1. Usuário clica em outra conta no dropdown
2. Frontend seta cookie x17-active-account-id
3. Proxima request: getAuthContext() le o cookie
4. Valida que usuário pertence a essa conta via account_users
5. Retorna novo AuthContext com o accountId da conta selecionada

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:

  1. Owner/Admin cria convite com email + role
  2. Token único gerado
  3. Convidado recebe link com token
  4. Ao aceitar: cria registro em account_users com o role específicado
ArquivoLinhaDescrição
src/middleware.ts1-59Route protection + security headers
src/lib/api/auth.ts1-58getAuthContext() + requireRole()
src/lib/supabase/client.ts1-8Browser client
src/lib/supabase/server.ts1-33Server client
src/lib/supabase/admin.ts1-14Admin/service role client
src/lib/supabase/middleware.ts1-49Session validation + redirects
src/lib/errors.ts16-21AuthError class
src/types/database.ts3AccountRole type