| package middleware |
|
|
| import ( |
| "errors" |
| "strings" |
|
|
| "github.com/Wei-Shaw/sub2api/internal/service" |
|
|
| "github.com/gin-gonic/gin" |
| ) |
|
|
| |
| func NewJWTAuthMiddleware(authService *service.AuthService, userService *service.UserService) JWTAuthMiddleware { |
| return JWTAuthMiddleware(jwtAuth(authService, userService)) |
| } |
|
|
| |
| func jwtAuth(authService *service.AuthService, userService *service.UserService) gin.HandlerFunc { |
| return func(c *gin.Context) { |
| |
| authHeader := c.GetHeader("Authorization") |
| if authHeader == "" { |
| AbortWithError(c, 401, "UNAUTHORIZED", "Authorization header is required") |
| return |
| } |
|
|
| |
| parts := strings.SplitN(authHeader, " ", 2) |
| if len(parts) != 2 || !strings.EqualFold(parts[0], "Bearer") { |
| AbortWithError(c, 401, "INVALID_AUTH_HEADER", "Authorization header format must be 'Bearer {token}'") |
| return |
| } |
|
|
| tokenString := strings.TrimSpace(parts[1]) |
| if tokenString == "" { |
| AbortWithError(c, 401, "EMPTY_TOKEN", "Token cannot be empty") |
| return |
| } |
|
|
| |
| claims, err := authService.ValidateToken(tokenString) |
| if err != nil { |
| if errors.Is(err, service.ErrTokenExpired) { |
| AbortWithError(c, 401, "TOKEN_EXPIRED", "Token has expired") |
| return |
| } |
| AbortWithError(c, 401, "INVALID_TOKEN", "Invalid token") |
| return |
| } |
|
|
| |
| user, err := userService.GetByID(c.Request.Context(), claims.UserID) |
| if err != nil { |
| AbortWithError(c, 401, "USER_NOT_FOUND", "User not found") |
| return |
| } |
|
|
| |
| if !user.IsActive() { |
| AbortWithError(c, 401, "USER_INACTIVE", "User account is not active") |
| return |
| } |
|
|
| |
| |
| if claims.TokenVersion != user.TokenVersion { |
| AbortWithError(c, 401, "TOKEN_REVOKED", "Token has been revoked (password changed)") |
| return |
| } |
|
|
| c.Set(string(ContextKeyUser), AuthSubject{ |
| UserID: user.ID, |
| Concurrency: user.Concurrency, |
| }) |
| c.Set(string(ContextKeyUserRole), user.Role) |
|
|
| c.Next() |
| } |
| } |
|
|
| |
|
|