Skip to content

Commit 8fa99bd

Browse files
authored
feat: fallback to jwt secret if alg is HS256 and the kid is not recognized (#2072)
Some customers may be using JWTs signed with the JWT secret but they may be advertising a `kid` claim for their own purposes. Auth should try to reasonably accept those JWTs.
1 parent 7ecb234 commit 8fa99bd

File tree

4 files changed

+28
-8
lines changed

4 files changed

+28
-8
lines changed

internal/api/auth.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,15 @@ func (a *API) parseJWTClaims(bearer string, r *http.Request) (context.Context, e
7777
token, err := p.ParseWithClaims(bearer, &AccessTokenClaims{}, func(token *jwt.Token) (interface{}, error) {
7878
if kid, ok := token.Header["kid"]; ok {
7979
if kidStr, ok := kid.(string); ok {
80-
return conf.FindPublicKeyByKid(kidStr, &config.JWT)
80+
key, err := conf.FindPublicKeyByKid(kidStr, &config.JWT)
81+
if err != nil {
82+
return nil, err
83+
}
84+
if key != nil {
85+
return key, nil
86+
}
87+
88+
// otherwise try to use fallback
8189
}
8290
}
8391
if alg, ok := token.Header["alg"]; ok {
@@ -86,7 +94,8 @@ func (a *API) parseJWTClaims(bearer string, r *http.Request) (context.Context, e
8694
return []byte(config.JWT.Secret), nil
8795
}
8896
}
89-
return nil, fmt.Errorf("missing kid")
97+
98+
return nil, fmt.Errorf("unrecognized JWT kid %v for algorithm %v", token.Header["kid"], token.Header["alg"])
9099
})
91100
if err != nil {
92101
return nil, apierrors.NewForbiddenError(apierrors.ErrorCodeBadJWT, "invalid JWT: unable to parse or verify signature, %v", err).WithInternalError(err)

internal/api/external.go

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -506,16 +506,26 @@ func (a *API) loadExternalState(ctx context.Context, r *http.Request) (context.C
506506
_, err := p.ParseWithClaims(state, &claims, func(token *jwt.Token) (interface{}, error) {
507507
if kid, ok := token.Header["kid"]; ok {
508508
if kidStr, ok := kid.(string); ok {
509-
return conf.FindPublicKeyByKid(kidStr, &config.JWT)
509+
key, err := conf.FindPublicKeyByKid(kidStr, &config.JWT)
510+
if err != nil {
511+
return nil, err
512+
}
513+
514+
if key != nil {
515+
return key, nil
516+
}
517+
518+
// otherwise try to use fallback
510519
}
511520
}
512521
if alg, ok := token.Header["alg"]; ok {
513522
if alg == jwt.SigningMethodHS256.Name {
514-
// preserve backward compatibility for cases where the kid is not set
523+
// preserve backward compatibility for cases where the kid is not set or potentially invalid but the key can be decoded with the secret
515524
return []byte(config.JWT.Secret), nil
516525
}
517526
}
518-
return nil, fmt.Errorf("missing kid")
527+
528+
return nil, fmt.Errorf("unrecognized JWT kid %v for algorithm %v", token.Header["kid"], token.Header["alg"])
519529
})
520530
if err != nil {
521531
return ctx, apierrors.NewBadRequestError(apierrors.ErrorCodeBadOAuthState, "OAuth callback with invalid state").WithInternalError(err)

internal/conf/jwk.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,5 +168,7 @@ func FindPublicKeyByKid(kid string, config *JWTConfiguration) (any, error) {
168168
if kid == config.KeyID {
169169
return []byte(config.Secret), nil
170170
}
171-
return nil, fmt.Errorf("invalid kid: %s", kid)
171+
172+
// don't return error, as a fallback key might be used
173+
return nil, nil
172174
}

internal/conf/jwk_test.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -241,8 +241,7 @@ func TestJwtKeys(t *testing.T) {
241241
}
242242
got, err := FindPublicKeyByKid("abc", jwtConfig)
243243
require.Nil(t, got)
244-
require.Error(t, err)
245-
require.Equal(t, "invalid kid: abc", err.Error())
244+
require.Nil(t, err)
246245
}
247246

248247
// FindPublicKeyByKid - GetSigningKey success

0 commit comments

Comments
 (0)