Files
aggios.app/backend/internal/api/middleware/unified_auth.go

105 lines
3.5 KiB
Go

package middleware
import (
"aggios-app/backend/internal/config"
"aggios-app/backend/internal/domain"
"context"
"log"
"net/http"
"strings"
"github.com/golang-jwt/jwt/v5"
)
// UnifiedAuthMiddleware valida JWT unificado e permite múltiplos tipos de usuários
func UnifiedAuthMiddleware(cfg *config.Config, allowedTypes ...domain.UserType) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Extrair token do header Authorization
authHeader := r.Header.Get("Authorization")
if authHeader == "" {
log.Printf("🚫 UnifiedAuth: Missing Authorization header")
http.Error(w, "Unauthorized: Missing token", http.StatusUnauthorized)
return
}
// Formato esperado: "Bearer <token>"
parts := strings.Split(authHeader, " ")
if len(parts) != 2 || parts[0] != "Bearer" {
log.Printf("🚫 UnifiedAuth: Invalid Authorization format")
http.Error(w, "Unauthorized: Invalid token format", http.StatusUnauthorized)
return
}
tokenString := parts[1]
// Parsear e validar token
token, err := jwt.ParseWithClaims(tokenString, &domain.UnifiedClaims{}, func(token *jwt.Token) (interface{}, error) {
return []byte(cfg.JWT.Secret), nil
})
if err != nil {
log.Printf("🚫 UnifiedAuth: Token parse error: %v", err)
http.Error(w, "Unauthorized: Invalid token", http.StatusUnauthorized)
return
}
claims, ok := token.Claims.(*domain.UnifiedClaims)
if !ok || !token.Valid {
log.Printf("🚫 UnifiedAuth: Invalid token claims")
http.Error(w, "Unauthorized: Invalid token", http.StatusUnauthorized)
return
}
// Verificar se o tipo de usuário é permitido
if len(allowedTypes) > 0 {
allowed := false
for _, allowedType := range allowedTypes {
if claims.UserType == allowedType {
allowed = true
break
}
}
if !allowed {
log.Printf("🚫 UnifiedAuth: User type %s not allowed (allowed: %v)", claims.UserType, allowedTypes)
http.Error(w, "Forbidden: Insufficient permissions", http.StatusForbidden)
return
}
}
// Adicionar informações ao contexto
ctx := r.Context()
ctx = context.WithValue(ctx, UserIDKey, claims.UserID)
ctx = context.WithValue(ctx, TenantIDKey, claims.TenantID)
ctx = context.WithValue(ctx, "email", claims.Email)
ctx = context.WithValue(ctx, "user_type", string(claims.UserType))
ctx = context.WithValue(ctx, "role", claims.Role)
// Para compatibilidade com handlers de portal que esperam CustomerIDKey
if claims.UserType == domain.UserTypeCustomer {
ctx = context.WithValue(ctx, CustomerIDKey, claims.UserID)
}
log.Printf("✅ UnifiedAuth: Authenticated user_id=%s, type=%s, role=%s, tenant=%s",
claims.UserID, claims.UserType, claims.Role, claims.TenantID)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
}
// RequireAgencyUser middleware que permite apenas usuários de agência (admin, colaborador)
func RequireAgencyUser(cfg *config.Config) func(http.Handler) http.Handler {
return UnifiedAuthMiddleware(cfg, domain.UserTypeAgency)
}
// RequireCustomer middleware que permite apenas clientes
func RequireCustomer(cfg *config.Config) func(http.Handler) http.Handler {
return UnifiedAuthMiddleware(cfg, domain.UserTypeCustomer)
}
// RequireAnyAuthenticated middleware que permite qualquer usuário autenticado
func RequireAnyAuthenticated(cfg *config.Config) func(http.Handler) http.Handler {
return UnifiedAuthMiddleware(cfg) // Sem filtro de tipo
}