- Add JWT authentication middleware with token validation - Implement user context extraction methods for user ID, email, and admin status - Create admin middleware to restrict access to admin-only routes - Add convenience method to combine authentication and admin authorization - Update auth middleware to handle token parsing, validation, and context setting - Enhance error handling for various authentication scenarios - Add new JWT service and related dependencies in go.mod
148 lines
3.4 KiB
Go
148 lines
3.4 KiB
Go
package middleware
|
|
|
|
import (
|
|
"net/http"
|
|
"strings"
|
|
|
|
"attune-heart-therapy/internal/services"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
// AuthMiddleware creates a middleware for JWT authentication
|
|
func AuthMiddleware(jwtService services.JWTService) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
// Get token from Authorization header
|
|
authHeader := c.GetHeader("Authorization")
|
|
if authHeader == "" {
|
|
c.JSON(http.StatusUnauthorized, gin.H{
|
|
"error": "Authorization header is required",
|
|
})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
// Check if header starts with "Bearer "
|
|
tokenParts := strings.SplitN(authHeader, " ", 2)
|
|
if len(tokenParts) != 2 || tokenParts[0] != "Bearer" {
|
|
c.JSON(http.StatusUnauthorized, gin.H{
|
|
"error": "Invalid authorization header format. Expected: Bearer <token>",
|
|
})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
tokenString := tokenParts[1]
|
|
if tokenString == "" {
|
|
c.JSON(http.StatusUnauthorized, gin.H{
|
|
"error": "Token is required",
|
|
})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
// Validate the token
|
|
claims, err := jwtService.ValidateToken(tokenString)
|
|
if err != nil {
|
|
c.JSON(http.StatusUnauthorized, gin.H{
|
|
"error": "Invalid or expired token",
|
|
})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
// Set user information in context for use in handlers
|
|
c.Set("user_id", claims.UserID)
|
|
c.Set("user_email", claims.Email)
|
|
c.Set("is_admin", claims.IsAdmin)
|
|
c.Set("jwt_claims", claims)
|
|
|
|
c.Next()
|
|
}
|
|
}
|
|
|
|
// GetUserIDFromContext extracts user ID from Gin context
|
|
func GetUserIDFromContext(c *gin.Context) (uint, bool) {
|
|
userID, exists := c.Get("user_id")
|
|
if !exists {
|
|
return 0, false
|
|
}
|
|
|
|
id, ok := userID.(uint)
|
|
return id, ok
|
|
}
|
|
|
|
// GetUserEmailFromContext extracts user email from Gin context
|
|
func GetUserEmailFromContext(c *gin.Context) (string, bool) {
|
|
email, exists := c.Get("user_email")
|
|
if !exists {
|
|
return "", false
|
|
}
|
|
|
|
emailStr, ok := email.(string)
|
|
return emailStr, ok
|
|
}
|
|
|
|
// IsAdminFromContext checks if user is admin from Gin context
|
|
func IsAdminFromContext(c *gin.Context) bool {
|
|
isAdmin, exists := c.Get("is_admin")
|
|
if !exists {
|
|
return false
|
|
}
|
|
|
|
admin, ok := isAdmin.(bool)
|
|
return ok && admin
|
|
}
|
|
|
|
// GetJWTClaimsFromContext extracts JWT claims from Gin context
|
|
func GetJWTClaimsFromContext(c *gin.Context) (*services.JWTClaims, bool) {
|
|
claims, exists := c.Get("jwt_claims")
|
|
if !exists {
|
|
return nil, false
|
|
}
|
|
|
|
jwtClaims, ok := claims.(*services.JWTClaims)
|
|
return jwtClaims, ok
|
|
}
|
|
|
|
// AdminMiddleware creates a middleware for admin authorization
|
|
// This middleware should be used after AuthMiddleware to ensure user is authenticated first
|
|
func AdminMiddleware() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
// Check if user is authenticated (should be set by AuthMiddleware)
|
|
userID, exists := GetUserIDFromContext(c)
|
|
if !exists || userID == 0 {
|
|
c.JSON(http.StatusUnauthorized, gin.H{
|
|
"error": "Authentication required",
|
|
})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
// Check if user has admin privileges
|
|
if !IsAdminFromContext(c) {
|
|
c.JSON(http.StatusForbidden, gin.H{
|
|
"error": "Admin privileges required",
|
|
})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
c.Next()
|
|
}
|
|
}
|
|
|
|
// RequireAdmin is a convenience function that combines auth and admin middleware
|
|
func RequireAdmin(jwtService services.JWTService) gin.HandlerFunc {
|
|
return gin.HandlerFunc(func(c *gin.Context) {
|
|
// First authenticate the user
|
|
AuthMiddleware(jwtService)(c)
|
|
if c.IsAborted() {
|
|
return
|
|
}
|
|
|
|
// Then check admin privileges
|
|
AdminMiddleware()(c)
|
|
})
|
|
}
|