package auth import ( "net/http" "strings" "github.com/gin-gonic/gin" "github.com/omnex/control-plane/api/internal/session" ) const ctxPrincipalKey = "omnex.principal" // Principal : identité authentifiée injectée dans le contexte de requête. type Principal struct { UserID string Username string Role Role SessionID string } // tokenFromRequest lit le JWT : cookie httpOnly en priorité, sinon Bearer. func tokenFromRequest(c *gin.Context) string { if ck, err := c.Request.Cookie(session.CookieName); err == nil && ck.Value != "" { return ck.Value } if t, ok := strings.CutPrefix(c.GetHeader("Authorization"), "Bearer "); ok { return t } return "" } // RequireAuth valide le JWT (signature) PUIS l'existence de la session Redis. // Un logout supprime la session : le JWT devient alors invalide avant expiration. func RequireAuth(iss *Issuer, mgr session.Manager) gin.HandlerFunc { return func(c *gin.Context) { raw := tokenFromRequest(c) if raw == "" { c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "authentification requise"}) return } claims, err := iss.Verify(raw) if err != nil { c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "token invalide"}) return } _, found, err := mgr.Get(c.Request.Context(), claims.SessionID) if err != nil { c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": "erreur serveur"}) return } if !found { c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "session expirée ou révoquée"}) return } c.Set(ctxPrincipalKey, Principal{UserID: claims.Subject, Username: claims.Username, Role: claims.Role, SessionID: claims.SessionID}) c.Next() } } // RequireRole impose un rôle minimal (admin > rôle demandé). func RequireRole(role Role) gin.HandlerFunc { return func(c *gin.Context) { p := PrincipalFrom(c) if p == nil || (p.Role != role && p.Role != RoleAdmin) { c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "accès refusé"}) return } c.Next() } } // PrincipalFrom récupère l'identité injectée par RequireAuth. func PrincipalFrom(c *gin.Context) *Principal { v, ok := c.Get(ctxPrincipalKey) if !ok { return nil } p, _ := v.(Principal) return &p }