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
| Campo | Tipo | Descrição |
|---|---|---|
agentId | string | Identificador único do agente |
role | string | Papel: agent, admin ou supervisor |
exp | number | Expiraçã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:
- Extrai token da URL ou header
- Divide em 3 partes: header, payload, assinatura
- Decodifica payload (base64 → JSON)
- Verifica campos obrigatórios (
agentId,role) - Verifica expiração (
exp > now) - 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
/loginse 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
| Papel | Acesso |
|---|---|
agent | Dashboard pessoal, chamadas, histórico próprio, perfil, configurações pessoais |
admin | Tudo do agente + gerenciamento de agentes, dashboard admin, analytics, configurações do sistema |
supervisor | Mesmo 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_SECRETperiodicamente - Implementar rate limiting nas tentativas de login
- Usar HTTPS/WSS em produção
- Considerar httpOnly cookies para armazenamento mais seguro