Ir para o conteúdo

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)

  1. Login (/api/auth/login) gera access + refresh.
  2. Refresh (/api/auth/refresh) valida assinatura/expiração, valida sessão persistida, revoga token anterior e retorna novo par de tokens.
  3. Reuso de refresh token já revogado dispara revogação da família de sessão.
  4. 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_id
  • token_hash
  • jti
  • family_id
  • expires_at
  • revoked_at
  • rotated_from_id
  • user_agent
  • ip

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 + email
  • POST /api/auth/register: 5 requisições / 10 minutos por IP
  • POST /api/auth/forgot-password: 5 requisições / 10 minutos por IP + email
  • POST /api/auth/reset-password: 5 requisições / 10 minutos por IP + email
  • POST /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

  1. admin - Administrador do sistema
  2. professional - Profissional de saúde
  3. 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/login
  • POST /api/auth/register
  • POST /api/auth/refresh
  • POST /api/auth/forgot-password
  • POST /api/auth/reset-password
  • POST /api/auth/verify-email
  • POST /api/auth/resend-verification
  • GET /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

  1. Senhas hasheadas: Bcrypt com salt automático
  2. Tokens com expiração: 24h para acesso, 7 dias para refresh
  3. Secrets em variáveis de ambiente: Nunca hardcoded
  4. HTTPS obrigatório em produção: Tokens não devem trafegar em HTTP
  5. 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

  1. Solicitar reset:

    POST /api/auth/forgot-password
    { "email": "user@example.com" }
    

  2. Backend:

  3. Gera token de reset
  4. Salva token no banco (tabela password_resets)
  5. Envia email com link

  6. Redefinir senha:

    POST /api/auth/reset-password
    {
      "token": "...",
      "new_password": "newpassword123"
    }
    

  7. Backend:

  8. Valida token
  9. Verifica expiração (geralmente 1 hora)
  10. Atualiza senha
  11. Invalida token

Verificação de Email

Fluxo

  1. Registro:
  2. Usuário se registra
  3. Backend cria usuário com email_verified = false
  4. Email de verificação é enviado

  5. Verificar:

    POST /api/auth/verify-email
    { "token": "..." }
    

  6. Backend:

  7. Valida token
  8. Marca email_verified = true
  9. Define email_verified_at

Referências