monorepo/cloud/maplefile-backend/internal/interface/http/middleware/jwt.go

74 lines
2.4 KiB
Go

// 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))
}
}