Skip to content

Commit b35cadd

Browse files
committed
feat: authentication middleware works with cluster provider
Signed-off-by: Calum Murray <[email protected]>
1 parent a88b830 commit b35cadd

File tree

2 files changed

+63
-8
lines changed

2 files changed

+63
-8
lines changed

pkg/http/authorization.go

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package http
22

33
import (
4+
"bytes"
45
"context"
6+
"encoding/json"
57
"fmt"
8+
"io"
69
"net/http"
710
"strings"
811

@@ -20,7 +23,42 @@ import (
2023

2124
type KubernetesApiTokenVerifier interface {
2225
// KubernetesApiVerifyToken TODO: clarify proper implementation
23-
KubernetesApiVerifyToken(ctx context.Context, token, audience string) (*authenticationapiv1.UserInfo, []string, error)
26+
KubernetesApiVerifyToken(ctx context.Context, token, audience, cluster string) (*authenticationapiv1.UserInfo, []string, error)
27+
}
28+
29+
// extractClusterFromRequest extracts cluster parameter from MCP request body
30+
func extractClusterFromRequest(r *http.Request) (string, error) {
31+
if r.Body == nil {
32+
return "", nil
33+
}
34+
35+
// Read the body
36+
body, err := io.ReadAll(r.Body)
37+
if err != nil {
38+
return "", err
39+
}
40+
41+
// Restore the body for downstream handlers
42+
r.Body = io.NopCloser(bytes.NewBuffer(body))
43+
44+
// Parse the MCP request
45+
var mcpRequest struct {
46+
Params struct {
47+
Arguments map[string]interface{} `json:"arguments"`
48+
} `json:"params"`
49+
}
50+
51+
if err := json.Unmarshal(body, &mcpRequest); err != nil {
52+
// If we can't parse the request, just return empty cluster (will use default)
53+
return "", nil
54+
}
55+
56+
// Extract cluster parameter
57+
if cluster, ok := mcpRequest.Params.Arguments["cluster"].(string); ok {
58+
return cluster, nil
59+
}
60+
61+
return "", nil
2462
}
2563

2664
// AuthorizationMiddleware validates the OAuth flow for protected resources.
@@ -128,7 +166,11 @@ func AuthorizationMiddleware(staticConfig *config.StaticConfig, oidcProvider *oi
128166
}
129167
// Kubernetes API Server TokenReview validation
130168
if err == nil && staticConfig.ValidateToken {
131-
err = claims.ValidateWithKubernetesApi(r.Context(), staticConfig.OAuthAudience, verifier)
169+
cluster, clusterErr := extractClusterFromRequest(r)
170+
if clusterErr != nil {
171+
klog.V(2).Infof("Failed to extract cluster from request, using default: %v", clusterErr)
172+
}
173+
err = claims.ValidateWithKubernetesApi(r.Context(), staticConfig.OAuthAudience, cluster, verifier)
132174
}
133175
if err != nil {
134176
klog.V(1).Infof("Authentication failed - JWT validation error: %s %s from %s, error: %v", r.Method, r.URL.Path, r.RemoteAddr, err)
@@ -198,9 +240,9 @@ func (c *JWTClaims) ValidateWithProvider(ctx context.Context, audience string, p
198240
return nil
199241
}
200242

201-
func (c *JWTClaims) ValidateWithKubernetesApi(ctx context.Context, audience string, verifier KubernetesApiTokenVerifier) error {
243+
func (c *JWTClaims) ValidateWithKubernetesApi(ctx context.Context, audience, cluster string, verifier KubernetesApiTokenVerifier) error {
202244
if verifier != nil {
203-
_, _, err := verifier.KubernetesApiVerifyToken(ctx, c.Token, audience)
245+
_, _, err := verifier.KubernetesApiVerifyToken(ctx, c.Token, audience, cluster)
204246
if err != nil {
205247
return fmt.Errorf("kubernetes API token validation error: %v", err)
206248
}

pkg/mcp/mcp.go

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,9 @@ func (s *Server) reloadKubernetesClusterProvider() error {
106106
}
107107

108108
// close the old provider
109-
s.p.Close()
109+
if s.p != nil {
110+
s.p.Close()
111+
}
110112

111113
s.p = p
112114

@@ -158,13 +160,24 @@ func (s *Server) ServeHTTP(httpServer *http.Server) *server.StreamableHTTPServer
158160
}
159161

160162
// KubernetesApiVerifyToken verifies the given token with the audience by
161-
// sending an TokenReview request to API Server.
162-
func (s *Server) KubernetesApiVerifyToken(ctx context.Context, token string, audience string) (*authenticationapiv1.UserInfo, []string, error) {
163+
// sending an TokenReview request to API Server for the specified cluster.
164+
func (s *Server) KubernetesApiVerifyToken(ctx context.Context, token string, audience string, cluster string) (*authenticationapiv1.UserInfo, []string, error) {
163165
if s.p == nil {
164166
return nil, nil, fmt.Errorf("kubernetes cluster provider is not initialized")
165167
}
166168

167-
return s.k.VerifyToken(ctx, token, audience)
169+
// Use provided cluster or default
170+
if cluster == "" {
171+
cluster = s.p.GetDefaultCluster()
172+
}
173+
174+
// Get the cluster manager for the specified cluster
175+
m, err := s.p.GetClusterManager(ctx, cluster)
176+
if err != nil {
177+
return nil, nil, err
178+
}
179+
180+
return m.VerifyToken(ctx, token, audience)
168181
}
169182

170183
func (s *Server) GetEnabledTools() []string {

0 commit comments

Comments
 (0)