@@ -2,14 +2,13 @@ package http
22
33import (
44 "context"
5- "encoding/base64"
6- "encoding/json"
75 "fmt"
86 "net/http"
97 "strings"
10- "time"
118
129 "github.com/coreos/go-oidc/v3/oidc"
10+ "github.com/go-jose/go-jose/v4"
11+ "github.com/go-jose/go-jose/v4/jwt"
1312 "k8s.io/klog/v2"
1413
1514 "github.com/manusa/kubernetes-mcp-server/pkg/mcp"
@@ -55,7 +54,10 @@ func AuthorizationMiddleware(requireOAuth bool, serverURL string, oidcProvider *
5554 // Validate the token offline for simple sanity check
5655 // Because missing expected audience and expired tokens must be
5756 // rejected already.
58- claims , err := validateJWTToken (token , audience )
57+ claims , err := ParseJWTClaims (token )
58+ if err == nil && claims != nil {
59+ err = claims .Validate (audience )
60+ }
5961 if err != nil {
6062 klog .V (1 ).Infof ("Authentication failed - JWT validation error: %s %s from %s, error: %v" , r .Method , r .URL .Path , r .RemoteAddr , err )
6163
@@ -117,11 +119,25 @@ func AuthorizationMiddleware(requireOAuth bool, serverURL string, oidcProvider *
117119 }
118120}
119121
122+ var allSignatureAlgorithms = []jose.SignatureAlgorithm {
123+ jose .EdDSA ,
124+ jose .HS256 ,
125+ jose .HS384 ,
126+ jose .HS512 ,
127+ jose .RS256 ,
128+ jose .RS384 ,
129+ jose .RS512 ,
130+ jose .ES256 ,
131+ jose .ES384 ,
132+ jose .ES512 ,
133+ jose .PS256 ,
134+ jose .PS384 ,
135+ jose .PS512 ,
136+ }
137+
120138type JWTClaims struct {
121- Issuer string `json:"iss"`
122- Audience any `json:"aud"`
123- ExpiresAt int64 `json:"exp"`
124- Scope string `json:"scope,omitempty"`
139+ jwt.Claims
140+ Scope string `json:"scope,omitempty"`
125141}
126142
127143func (c * JWTClaims ) GetScopes () []string {
@@ -131,66 +147,21 @@ func (c *JWTClaims) GetScopes() []string {
131147 return strings .Fields (c .Scope )
132148}
133149
134- func (c * JWTClaims ) ContainsAudience (audience string ) bool {
135- switch aud := c .Audience .(type ) {
136- case string :
137- return aud == audience
138- case []interface {}:
139- for _ , a := range aud {
140- if str , ok := a .(string ); ok && str == audience {
141- return true
142- }
143- }
144- case []string :
145- for _ , a := range aud {
146- if a == audience {
147- return true
148- }
149- }
150- }
151- return false
152- }
153-
154- // validateJWTToken validates basic JWT claims without signature verification and returns the claims
155- func validateJWTToken (token , audience string ) (* JWTClaims , error ) {
156- parts := strings .Split (token , "." )
157- if len (parts ) != 3 {
158- return nil , fmt .Errorf ("invalid JWT token format" )
159- }
160-
161- claims , err := parseJWTClaims (parts [1 ])
162- if err != nil {
163- return nil , fmt .Errorf ("failed to parse JWT claims: %v" , err )
164- }
165-
166- if claims .ExpiresAt > 0 && time .Now ().Unix () > claims .ExpiresAt {
167- return nil , fmt .Errorf ("token expired" )
168- }
169-
170- if ! claims .ContainsAudience (audience ) {
171- return nil , fmt .Errorf ("token audience mismatch: %v" , claims .Audience )
172- }
173-
174- return claims , nil
150+ // Validate Checks if the JWT claims are valid and if the audience matches the expected one.
151+ func (c * JWTClaims ) Validate (audience string ) error {
152+ return c .Claims .Validate (jwt.Expected {
153+ AnyAudience : jwt.Audience {audience },
154+ })
175155}
176156
177- func parseJWTClaims (payload string ) (* JWTClaims , error ) {
178- // Add padding if needed
179- if len (payload )% 4 != 0 {
180- payload += strings .Repeat ("=" , 4 - len (payload )% 4 )
181- }
182-
183- decoded , err := base64 .URLEncoding .DecodeString (payload )
157+ func ParseJWTClaims (token string ) (* JWTClaims , error ) {
158+ tkn , err := jwt .ParseSigned (token , allSignatureAlgorithms )
184159 if err != nil {
185- return nil , fmt .Errorf ("failed to decode JWT payload : %v " , err )
160+ return nil , fmt .Errorf ("failed to parse JWT token : %w " , err )
186161 }
187-
188- var claims JWTClaims
189- if err := json .Unmarshal (decoded , & claims ); err != nil {
190- return nil , fmt .Errorf ("failed to unmarshal JWT claims: %v" , err )
191- }
192-
193- return & claims , nil
162+ claims := & JWTClaims {}
163+ err = tkn .UnsafeClaimsWithoutVerification (claims )
164+ return claims , err
194165}
195166
196167func validateTokenWithOIDC (ctx context.Context , provider * oidc.Provider , token , audience string ) error {
0 commit comments