package middleware import ( "fmt" "net/http" "strings" "github.com/flatrender/notification-svc/internal/models" "github.com/gin-gonic/gin" "github.com/golang-jwt/jwt/v5" "github.com/google/uuid" ) const ( CtxUserID = "user_id" CtxTenantID = "tenant_id" CtxIsAdmin = "is_admin" ) func JWTAuth(secret string) gin.HandlerFunc { return func(c *gin.Context) { hdr := c.GetHeader("Authorization") if !strings.HasPrefix(hdr, "Bearer ") { c.AbortWithStatusJSON(http.StatusUnauthorized, models.APIError{Code: "unauthorized", Message: "missing bearer token"}) return } token, err := jwt.Parse(hdr[7:], func(t *jwt.Token) (interface{}, error) { if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok { return nil, jwt.ErrSignatureInvalid } return []byte(secret), nil }) if err != nil || !token.Valid { c.AbortWithStatusJSON(http.StatusUnauthorized, models.APIError{Code: "unauthorized", Message: "invalid token"}) return } claims, _ := token.Claims.(jwt.MapClaims) userID, _ := uuid.Parse(fmt.Sprintf("%v", claims["sub"])) tenantID, _ := uuid.Parse(fmt.Sprintf("%v", claims["tenant_id"])) isAdmin := false switch v := claims["is_admin"].(type) { case bool: isAdmin = v case string: isAdmin = v == "true" } c.Set(CtxUserID, userID) c.Set(CtxTenantID, tenantID) c.Set(CtxIsAdmin, isAdmin) c.Next() } } func RequireAdmin() gin.HandlerFunc { return func(c *gin.Context) { v, _ := c.Get(CtxIsAdmin) b, _ := v.(bool) if !b { c.AbortWithStatusJSON(http.StatusForbidden, models.APIError{Code: "forbidden", Message: "admin required"}) return } c.Next() } } func GetUserID(c *gin.Context) uuid.UUID { v, _ := c.Get(CtxUserID) id, _ := v.(uuid.UUID) return id } func GetTenantID(c *gin.Context) uuid.UUID { v, _ := c.Get(CtxTenantID) id, _ := v.(uuid.UUID) return id }