package middleware import ( "context" "net/http" "strings" "quake2dropzone/internal/auth" ) type contextKey string const ClaimsKey contextKey = "claims" // AuthRequired validates the JWT from the Authorization: Bearer header. // On success it injects the claims into the request context. func AuthRequired(jwtSecret string) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { header := r.Header.Get("Authorization") if !strings.HasPrefix(header, "Bearer ") { http.Error(w, "unauthorized", http.StatusUnauthorized) return } tokenStr := strings.TrimPrefix(header, "Bearer ") claims, err := auth.ParseJWT(jwtSecret, tokenStr) if err != nil { http.Error(w, "invalid token", http.StatusUnauthorized) return } ctx := context.WithValue(r.Context(), ClaimsKey, claims) next.ServeHTTP(w, r.WithContext(ctx)) }) } } // AdminRequired enforces the "admin" role in the JWT claims. // Must be used after AuthRequired. func AdminRequired(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { claims, ok := r.Context().Value(ClaimsKey).(*auth.Claims) if !ok || claims.Role != "admin" { http.Error(w, "forbidden", http.StatusForbidden) return } next.ServeHTTP(w, r) }) } // GetClaims retrieves the claims from the request context. func GetClaims(r *http.Request) *auth.Claims { claims, _ := r.Context().Value(ClaimsKey).(*auth.Claims) return claims }