// monorepo/cloud/maplefile-backend/internal/maplefile/interface/http/middleware/jwt.go package middleware import ( "context" "net/http" "strings" "go.uber.org/zap" "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config/constants" "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror" ) func (mid *middleware) JWTProcessorMiddleware(fn http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() // Extract the Authorization header reqToken := r.Header.Get("Authorization") // Validate that Authorization header is present if reqToken == "" { problem := httperror.NewUnauthorizedError("Authorization not set") problem.WithInstance(r.URL.Path). WithTraceID(httperror.ExtractRequestID(r)) httperror.RespondWithProblem(w, problem) return } // Extract the token from the Authorization header // Support both "Bearer" (RFC 6750 standard) and "JWT" schemes for compatibility var token string if strings.HasPrefix(reqToken, "Bearer ") { token = strings.TrimPrefix(reqToken, "Bearer ") } else if strings.HasPrefix(reqToken, "JWT ") { token = strings.TrimPrefix(reqToken, "JWT ") } else { problem := httperror.NewBadRequestError("Not properly formatted authorization header") problem.WithInstance(r.URL.Path). WithTraceID(httperror.ExtractRequestID(r)) httperror.RespondWithProblem(w, problem) return } // Validate the token is not empty after prefix removal if token == "" { problem := httperror.NewBadRequestError("Not properly formatted authorization header") problem.WithInstance(r.URL.Path). WithTraceID(httperror.ExtractRequestID(r)) httperror.RespondWithProblem(w, problem) return } // Process the JWT token sessionID, err := mid.jwt.ProcessJWTToken(token) if err != nil { // Log the actual error for debugging but return generic message to client mid.logger.Error("JWT processing failed", zap.Error(err)) problem := httperror.NewUnauthorizedError("Invalid or expired token") problem.WithInstance(r.URL.Path). WithTraceID(httperror.ExtractRequestID(r)) httperror.RespondWithProblem(w, problem) return } // Update our context to save our JWT token content information ctx = context.WithValue(ctx, constants.SessionIsAuthorized, true) ctx = context.WithValue(ctx, constants.SessionID, sessionID) // Flow to the next middleware with our JWT token saved fn(w, r.WithContext(ctx)) } }