@@ -2,14 +2,13 @@ package http
2
2
3
3
import (
4
4
"context"
5
- "encoding/base64"
6
- "encoding/json"
7
5
"fmt"
8
6
"net/http"
9
7
"strings"
10
- "time"
11
8
12
9
"github.com/coreos/go-oidc/v3/oidc"
10
+ "github.com/go-jose/go-jose/v4"
11
+ "github.com/go-jose/go-jose/v4/jwt"
13
12
"k8s.io/klog/v2"
14
13
15
14
"github.com/manusa/kubernetes-mcp-server/pkg/mcp"
@@ -55,7 +54,10 @@ func AuthorizationMiddleware(requireOAuth bool, serverURL string, oidcProvider *
55
54
// Validate the token offline for simple sanity check
56
55
// Because missing expected audience and expired tokens must be
57
56
// 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
+ }
59
61
if err != nil {
60
62
klog .V (1 ).Infof ("Authentication failed - JWT validation error: %s %s from %s, error: %v" , r .Method , r .URL .Path , r .RemoteAddr , err )
61
63
@@ -117,11 +119,25 @@ func AuthorizationMiddleware(requireOAuth bool, serverURL string, oidcProvider *
117
119
}
118
120
}
119
121
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
+
120
138
type 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"`
125
141
}
126
142
127
143
func (c * JWTClaims ) GetScopes () []string {
@@ -131,66 +147,21 @@ func (c *JWTClaims) GetScopes() []string {
131
147
return strings .Fields (c .Scope )
132
148
}
133
149
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
+ })
175
155
}
176
156
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 )
184
159
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 )
186
161
}
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
194
165
}
195
166
196
167
func validateTokenWithOIDC (ctx context.Context , provider * oidc.Provider , token , audience string ) error {
0 commit comments