Arquitetura

Autenticação

Fluxo de autenticação JWT, tokens e controle de acesso.

Autenticação

O stCall utiliza JWT (JSON Web Tokens) para autenticação, com o segredo compartilhado entre o frontend e o servidor WebSocket.

Fluxo Completo

1. Login (Frontend)
   Usuário → E-mail + Senha → API /auth/login
                                      │
2. Geração do Token                   ▼
   Backend valida credenciais → Gera JWT (HS256)
                                      │
3. Armazenamento                      ▼
   Token salvo no navegador (Pinia + localStorage)
                                      │
4. Conexão WebSocket                  ▼
   ws://host:3001/?token=<JWT>
                                      │
5. Validação (stcall-ws)              ▼
   Extrai token → Valida → Registra agente

Geração do Token

O token é gerado pelo backend do Nuxt no endpoint de login:

const payload = {
  agentId: "agent-123",
  role: "agent",       // "agent" | "admin" | "supervisor"
  exp: Math.floor(Date.now() / 1000) + (8 * 60 * 60) // 8 horas
}

const token = jwt.sign(payload, JWT_SECRET, { algorithm: 'HS256' })

Campos do Payload

CampoTipoDescrição
agentIdstringIdentificador único do agente
rolestringPapel: agent, admin ou supervisor
expnumberExpiração (Unix timestamp)

Armazenamento no Navegador

O authStore (Pinia) gerencia o estado de autenticação:

// Login salva token e dados do usuário
this.token = response.token
this.user = response.user
this.isAuthenticated = true

// Persiste no localStorage via useTokenStorage
tokenStorage.saveAuthData(response.token, response.user)

Opções de armazenamento:

  • Pinia store - Estado reativo (principal)
  • localStorage - Persistência entre sessões
  • sessionStorage - Limpo ao fechar navegador

Conexão WebSocket

O token é enviado ao conectar no servidor WebSocket:

const ws = new WebSocket(`ws://localhost:3001/?token=${token}`)

Também suporta header Authorization:

Authorization: Bearer <JWT>

Validação no Servidor

O stcall-ws valida o token em cada conexão:

  1. Extrai token da URL ou header
  2. Divide em 3 partes: header, payload, assinatura
  3. Decodifica payload (base64 → JSON)
  4. Verifica campos obrigatórios (agentId, role)
  5. Verifica expiração (exp > now)
  6. Verifica assinatura (HMAC-SHA256 com segredo compartilhado)

Se válido, faz upgrade para WebSocket com metadados do agente:

server.upgrade(req, {
  data: {
    agentId: payload.agentId,
    role: payload.role,
    authenticated: true
  }
})
O servidor WebSocket não armazena o token JWT. Apenas os metadados extraídos (agentId, role) ficam em memória enquanto a conexão estiver ativa.

Controle de Acesso

Middleware de Rota (Frontend)

O middleware auth.global.ts protege todas as rotas:

  • Verifica se o usuário está autenticado
  • Redireciona para /login se não estiver
  • Exceção: a rota /login é pública

O middleware admin.ts protege rotas administrativas:

  • Verifica se authStore.isAdmin é true
  • Redireciona para / se não for admin

Papéis

PapelAcesso
agentDashboard pessoal, chamadas, histórico próprio, perfil, configurações pessoais
adminTudo do agente + gerenciamento de agentes, dashboard admin, analytics, configurações do sistema
supervisorMesmo acesso que admin

Verificação no Frontend

// No authStore
isAdmin: (state) => state.user?.role === 'admin' || state.user?.role === 'supervisor'
isAgent: (state) => state.user?.role === 'agent'
<!-- Na template -->
<TabPanel v-if="authStore.isAdmin">
  <!-- Conteúdo admin -->
</TabPanel>

Segurança

JWT_SECRET

  • Deve ser idêntico no frontend e no stcall-ws
  • Use uma string aleatória longa e segura
  • Nunca exponha em código-fonte ou logs
  • Armazene via variável de ambiente

Expiração

  • Tokens expiram em 8 horas
  • Após expiração, o servidor WebSocket rejeita a conexão
  • Frontend detecta a rejeição e redireciona para login

Boas Práticas

  • Rotacionar o JWT_SECRET periodicamente
  • Implementar rate limiting nas tentativas de login
  • Usar HTTPS/WSS em produção
  • Considerar httpOnly cookies para armazenamento mais seguro
Copyright © 2026