Backend - Autenticação e Autorização¶
Visão Geral¶
O sistema utiliza JWT (JSON Web Tokens) para autenticação stateless. Não há sessões no servidor, o que permite escalabilidade horizontal e funciona bem com múltiplos clientes (web e mobile).
JWT (JSON Web Tokens)¶
Estrutura do Token¶
O JWT contém as seguintes claims:
type JWTClaims struct {
UserID uint `json:"user_id"`
UserType string `json:"user_type"` // "admin", "professional", "client"
Email string `json:"email"`
jwt.RegisteredClaims
}
RegisteredClaims inclui:
- ExpiresAt - Data de expiração
- IssuedAt - Data de emissão
Geração de Tokens¶
Token de Acesso (JWT):
- Duração: 24 horas
- Secret: JWT_SECRET (variável de ambiente)
- Algoritmo: HS256
Refresh Token:
- Duração: 7 dias
- Secret: JWT_REFRESH_SECRET (variável de ambiente)
- Algoritmo: HS256
- Claims: Apenas user_id (sem user_type ou email)
Localização do Código¶
- Geração:
internal/utils/jwt.go - Validação:
internal/middleware/auth/auth.go
Fluxo de Autenticação¶
1. Login¶
POST /api/auth/login
{
"email": "user@example.com",
"password": "password123"
}
Resposta:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expires_in": 86400,
"refresh_expires_in": 604800,
"user": {
"id": 1,
"email": "user@example.com",
"name": "Nome",
"user_type": "client"
}
}
Processo: 1. Backend valida email e senha 2. Senha é verificada com bcrypt 3. Tokens são gerados 4. Tokens são retornados ao cliente
2. Uso do Token¶
Header obrigatório:
Authorization: Bearer <token>
Exemplo:
curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
https://api.example.com/api/cycles
3. Refresh Token¶
Quando o token expira (24 horas), o cliente pode usar o refresh token:
POST /api/auth/refresh
{
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
Resposta:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expires_in": 86400,
"refresh_expires_in": 604800
}
4. Logout¶
POST /api/auth/logout
Authorization: Bearer <access_token>
Body opcional:
{
"refresh_token": "..."
}
- Sem
refresh_token: revoga todas as sessões ativas do usuário. - Com
refresh_token: revoga apenas a sessão correspondente.
Auth Lifecycle (estado atual)¶
- Login (
/api/auth/login) gera access + refresh. - Refresh (
/api/auth/refresh) valida assinatura/expiração, valida sessão persistida, revoga token anterior e retorna novo par de tokens. - Reuso de refresh token já revogado dispara revogação da família de sessão.
- Logout (
/api/auth/logout) revoga sessão atual ou todas as sessões ativas.
Persistência de refresh token¶
- Tabela:
refresh_sessions - Campos principais:
user_idtoken_hashjtifamily_idexpires_atrevoked_atrotated_from_iduser_agentip
O refresh token nunca é persistido em texto puro; apenas hash.
Rate Limit de autenticação¶
O backend aplica rate limit nos endpoints públicos de autenticação:
POST /api/auth/login: 10 requisições / 5 minutos por IP + emailPOST /api/auth/register: 5 requisições / 10 minutos por IPPOST /api/auth/forgot-password: 5 requisições / 10 minutos por IP + emailPOST /api/auth/reset-password: 5 requisições / 10 minutos por IP + emailPOST /api/auth/resend-verification: 5 requisições / 10 minutos por IP + email- OAuth mantém política própria mais restritiva
Middleware de Autenticação¶
AuthMiddleware¶
Localização: internal/middleware/auth/auth.go
Funcionalidade:
1. Extrai token do header Authorization
2. Valida formato: Bearer <token>
3. Valida assinatura do token
4. Verifica expiração
5. Adiciona claims ao contexto do Echo
Uso:
api := e.Group("/api")
api.Use(authMiddleware.AuthMiddleware())
Dados adicionados ao contexto:
- user_id (uint)
- user_type (string)
- email (string)
Exemplo de uso no handler:
func (h *Handler) GetCycles(c echo.Context) error {
userID := c.Get("user_id").(uint)
userType := c.Get("user_type").(string)
// ...
}
Papéis (Roles) e Permissões¶
Tipos de Usuário¶
- admin - Administrador do sistema
- professional - Profissional de saúde
- client - Cliente (paciente)
RoleMiddleware¶
Localização: internal/middleware/role/role.go
Funcionalidade:
- Verifica se o user_type do token está na lista de roles permitidas
Uso:
adminRoutes := api.Group("")
adminRoutes.Use(role.RoleMiddleware([]string{"admin"}))
Exemplo:
// Apenas admin e professional
usersRoutes.Use(role.RoleMiddleware([]string{"admin", "professional"}))
// Apenas admin
adminRoutes.Use(role.RoleMiddleware([]string{"admin"}))
Verificação de Aprovação de Perfil¶
ProfileApprovalMiddleware
Localização: internal/middleware/profile/profile.go
Funcionalidade: - Verifica se profissional tem perfil aprovado - Aplica apenas a rotas de profissionais que requerem aprovação
Uso:
approvedRoutes := api.Group("")
approvedRoutes.Use(role.RoleMiddleware([]string{"professional"}))
approvedRoutes.Use(profile.ProfileApprovalMiddleware())
Rotas Públicas vs Protegidas¶
Rotas Públicas¶
Não requerem autenticação:
POST /api/auth/loginPOST /api/auth/registerPOST /api/auth/refreshPOST /api/auth/forgot-passwordPOST /api/auth/reset-passwordPOST /api/auth/verify-emailPOST /api/auth/resend-verificationGET /api/plans(listagem pública de planos)
Rotas Protegidas¶
Requerem autenticação (JWT):
- Todas as rotas em
/api/*(exceto as públicas acima) POST /api/auth/logout- Middleware de autenticação aplicado em
routes.go:api := e.Group("/api") api.Use(authMiddleware.AuthMiddleware())
Rotas por Role¶
Admin:
- /api/admin/* - Gerenciamento do Método Billings
- /api/users - Gerenciamento de usuários
- /api/plans (POST, PUT, DELETE) - Gerenciamento de planos
Professional:
- /api/patients - Gerenciamento de pacientes
- /api/professional/* - Funcionalidades profissionais
- Requer perfil aprovado para algumas rotas
Client:
- /api/client/* - Funcionalidades de cliente
- /api/cycles - Gerenciamento de ciclos
- /api/observations - Registro de observações
Geral (autenticado):
- /api/accounts - Contas
- /api/subscriptions - Assinaturas
- /api/messages - Mensagens
- /api/appointments - Agendamentos
Impactos para Frontend e Mobile¶
Frontend Web¶
Armazenamento:
localStorage.setItem('token', token)
localStorage.setItem('refresh_token', refreshToken)
Uso:
// Axios interceptor adiciona automaticamente
api.interceptors.request.use((config) => {
const token = localStorage.getItem('token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
})
Tratamento de 401:
api.interceptors.response.use(
(response) => response,
(error) => {
if (error.response?.status === 401) {
localStorage.removeItem('token')
localStorage.removeItem('refresh_token')
window.location.href = '/login'
}
return Promise.reject(error)
}
)
Mobile¶
Armazenamento:
import * as SecureStore from 'expo-secure-store'
await SecureStore.setItemAsync('token', token)
await SecureStore.setItemAsync('refresh_token', refreshToken)
Uso:
api.interceptors.request.use(async (config) => {
const token = await SecureStore.getItemAsync('token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
})
Tratamento de 401:
api.interceptors.response.use(
(response) => response,
async (error) => {
if (error.response?.status === 401) {
await SecureStore.deleteItemAsync('token')
await SecureStore.deleteItemAsync('refresh_token')
// Navegar para tela de login
}
return Promise.reject(error)
}
)
Segurança¶
Boas Práticas Implementadas¶
- Senhas hasheadas: Bcrypt com salt automático
- Tokens com expiração: 24h para acesso, 7 dias para refresh
- Secrets em variáveis de ambiente: Nunca hardcoded
- HTTPS obrigatório em produção: Tokens não devem trafegar em HTTP
- Validação de assinatura: Tokens são validados a cada requisição
Recomendações Futuras¶
- Rate limiting: Limitar tentativas de login
- Rotação de secrets: Plano para rotacionar JWT_SECRET periodicamente
- Refresh token rotation: Invalidar refresh token após uso
- 2FA: Autenticação de dois fatores (futuro)
Recuperação de Senha¶
Fluxo¶
-
Solicitar reset:
POST /api/auth/forgot-password { "email": "user@example.com" } -
Backend:
- Gera token de reset
- Salva token no banco (tabela
password_resets) -
Envia email com link
-
Redefinir senha:
POST /api/auth/reset-password { "token": "...", "new_password": "newpassword123" } -
Backend:
- Valida token
- Verifica expiração (geralmente 1 hora)
- Atualiza senha
- Invalida token
Verificação de Email¶
Fluxo¶
- Registro:
- Usuário se registra
- Backend cria usuário com
email_verified = false -
Email de verificação é enviado
-
Verificar:
POST /api/auth/verify-email { "token": "..." } -
Backend:
- Valida token
- Marca
email_verified = true - Define
email_verified_at