diff --git a/backend.go b/backend.go index cdd8665e..85041de0 100644 --- a/backend.go +++ b/backend.go @@ -11,6 +11,7 @@ import ( "github.com/hashicorp/cap/jwt" "github.com/hashicorp/cap/oidc" + "github.com/hashicorp/go-multierror" "github.com/hashicorp/vault/sdk/framework" "github.com/hashicorp/vault/sdk/logical" "github.com/patrickmn/go-cache" @@ -145,15 +146,33 @@ func (b *jwtAuthBackend) jwtValidator(config *jwtConfig) (*jwt.Validator, error) var err error var keySet jwt.KeySet + var keySets []jwt.KeySet // Configure the key set for the validator switch config.authType() { case JWKS: keySet, err = jwt.NewJSONWebKeySet(b.providerCtx, config.JWKSURL, config.JWKSCAPEM) + keySets = []jwt.KeySet{keySet} + case MultiJWKS: + pairs, pairsErr := NewJWKSPairsConfig(config) + if pairsErr != nil { + return nil, pairsErr + } + + for _, p := range pairs { + keySet, keySetErr := jwt.NewJSONWebKeySet(b.providerCtx, p.JWKSUrl, p.JWKSCAPEM) + if keySetErr != nil { + err = multierror.Append(err, keySetErr) + continue + } + keySets = append(keySets, keySet) + } case StaticKeys: keySet, err = jwt.NewStaticKeySet(config.ParsedJWTPubKeys) + keySets = []jwt.KeySet{keySet} case OIDCDiscovery: keySet, err = jwt.NewOIDCDiscoveryKeySet(b.providerCtx, config.OIDCDiscoveryURL, config.OIDCDiscoveryCAPEM) + keySets = []jwt.KeySet{keySet} default: return nil, errors.New("unsupported config type") } @@ -162,7 +181,7 @@ func (b *jwtAuthBackend) jwtValidator(config *jwtConfig) (*jwt.Validator, error) return nil, fmt.Errorf("keyset configuration error: %w", err) } - validator, err := jwt.NewValidator(keySet) + validator, err := jwt.NewValidator(keySets...) if err != nil { return nil, fmt.Errorf("JWT validator configuration error: %w", err) } diff --git a/backend_test.go b/backend_test.go new file mode 100644 index 00000000..88d023ad --- /dev/null +++ b/backend_test.go @@ -0,0 +1,54 @@ +package jwtauth + +import ( + "context" + "testing" + + "github.com/hashicorp/cap/jwt" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_jwtAuthBackend_jwtValidator(t *testing.T) { + type args struct { + config *jwtConfig + } + tests := []struct { + name string + args args + want *jwt.Validator + expectedErr string + }{ + { + name: "invalid ca pem", + args: args{ + config: &jwtConfig{ + JWKSPairs: []interface{}{ + map[string]any{ + "jwks_url": "https://www.foobar.com/something", + "jwks_ca_pem": "defg", + }, + map[string]any{ + "jwks_url": "https://www.barbaz.com/something", + "jwks_ca_pem": "", + }, + }, + }, + }, + expectedErr: "could not parse CA PEM value successfully", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + b := &jwtAuthBackend{} + b.providerCtx = context.TODO() + + got, err := b.jwtValidator(tt.args.config) + if tt.expectedErr != "" { + require.ErrorContains(t, err, tt.expectedErr) + return + } + assert.Equalf(t, tt.want, got, "jwtValidator(%v)", tt.args.config) + }) + } +} diff --git a/go.mod b/go.mod index e4122ac2..66a294ec 100644 --- a/go.mod +++ b/go.mod @@ -4,10 +4,11 @@ go 1.20 require ( github.com/go-test/deep v1.1.0 - github.com/hashicorp/cap v0.4.1 + github.com/hashicorp/cap v0.5.0 github.com/hashicorp/errwrap v1.1.0 github.com/hashicorp/go-cleanhttp v0.5.2 github.com/hashicorp/go-hclog v1.6.2 + github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-secure-stdlib/base62 v0.1.2 github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 github.com/hashicorp/go-sockaddr v1.0.6 @@ -54,7 +55,6 @@ require ( github.com/hashicorp/go-immutable-radix v1.3.1 // indirect github.com/hashicorp/go-kms-wrapping/entropy/v2 v2.0.0 // indirect github.com/hashicorp/go-kms-wrapping/v2 v2.0.8 // indirect - github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-plugin v1.5.2 // indirect github.com/hashicorp/go-retryablehttp v0.7.1 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect diff --git a/go.sum b/go.sum index ffcc48b4..3c3d1988 100644 --- a/go.sum +++ b/go.sum @@ -117,8 +117,8 @@ github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfF github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= -github.com/hashicorp/cap v0.4.1 h1:LVYrTLbPV8W6DPwIm/zC/fbc4UTpCQ7nJhCAPshLuG4= -github.com/hashicorp/cap v0.4.1/go.mod h1:oOoohCNd2JAgfvLz2NpFJTZiZ6CqH9dW8dZ2js52lGA= +github.com/hashicorp/cap v0.5.0 h1:YIlAYxdXXtx2IL1JDvP2OyEl55Ooi0yl573kSB9Orlw= +github.com/hashicorp/cap v0.5.0/go.mod h1:IAy00Er+ZFpMo+5x6B4bkO2HgpzgrkfsuDWMmHAuKUE= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= diff --git a/jwks_pairs.go b/jwks_pairs.go new file mode 100644 index 00000000..8924b483 --- /dev/null +++ b/jwks_pairs.go @@ -0,0 +1,42 @@ +package jwtauth + +import ( + "fmt" + + "github.com/mitchellh/mapstructure" +) + +type JWKSPair struct { + JWKSUrl string `mapstructure:"jwks_url"` + JWKSCAPEM string `mapstructure:"jwks_ca_pem"` +} + +func NewJWKSPairsConfig(jc *jwtConfig) ([]*JWKSPair, error) { + if len(jc.JWKSPairs) <= 0 { + return nil, nil + } + + pairs := make([]*JWKSPair, 0, len(jc.JWKSPairs)) + for i := 0; i < len(jc.JWKSPairs); i++ { + pairsMap, ok := jc.JWKSPairs[i].(map[string]interface{}) + if !ok { + return nil, fmt.Errorf("jwks_pairs must be provided as a list of json objects with the fields jwks_url and jwks_ca_pem") + } + jp, err := Initialize(pairsMap) + if err != nil { + return nil, err + } + pairs = append(pairs, jp) + } + + return pairs, nil +} + +func Initialize(jp map[string]interface{}) (*JWKSPair, error) { + var newJp JWKSPair + if err := mapstructure.Decode(jp, &newJp); err != nil { + return nil, err + } + + return &newJp, nil +} diff --git a/path_config.go b/path_config.go index c4acf0a1..21fed23c 100644 --- a/path_config.go +++ b/path_config.go @@ -9,6 +9,7 @@ import ( "crypto/tls" "crypto/x509" "errors" + "fmt" "net/http" "strings" @@ -72,6 +73,10 @@ func pathConfig(b *jwtAuthBackend) *framework.Path { Type: framework.TypeString, Description: "The CA certificate or chain of certificates, in PEM format, to use to validate connections to the JWKS URL. If not set, system certificates are used.", }, + "jwks_pairs": { + Type: framework.TypeSlice, + Description: `Set of JWKS Url and CA certificate (or chain of certificates) pairs. CA certificates must be in PEM format. Cannot be used with "jwks_url" or "jwks_ca_pem".`, + }, "default_role": { Type: framework.TypeLowerCaseString, Description: "The default role to use if none is provided during login. If not set, a role is required during login.", @@ -200,6 +205,7 @@ func (b *jwtAuthBackend) pathConfigRead(ctx context.Context, req *logical.Reques "jwt_validation_pubkeys": config.JWTValidationPubKeys, "jwt_supported_algs": config.JWTSupportedAlgs, "jwks_url": config.JWKSURL, + "jwks_pairs": config.JWKSPairs, "jwks_ca_pem": config.JWKSCAPEM, "bound_issuer": config.BoundIssuer, "provider_config": providerConfig, @@ -219,6 +225,7 @@ func (b *jwtAuthBackend) pathConfigWrite(ctx context.Context, req *logical.Reque OIDCResponseMode: d.Get("oidc_response_mode").(string), OIDCResponseTypes: d.Get("oidc_response_types").([]string), JWKSURL: d.Get("jwks_url").(string), + JWKSPairs: d.Get("jwks_pairs").([]interface{}), JWKSCAPEM: d.Get("jwks_ca_pem").(string), DefaultRole: d.Get("default_role").(string), JWTValidationPubKeys: d.Get("jwt_validation_pubkeys").([]string), @@ -255,10 +262,14 @@ func (b *jwtAuthBackend) pathConfigWrite(ctx context.Context, req *logical.Reque if config.JWKSURL != "" { methodCount++ } + if len(config.JWKSPairs) > 0 { + methodCount++ + } + var jwksPairs []*JWKSPair switch { case methodCount != 1: - return logical.ErrorResponse("exactly one of 'jwt_validation_pubkeys', 'jwks_url' or 'oidc_discovery_url' must be set"), nil + return logical.ErrorResponse("exactly one of 'jwt_validation_pubkeys', 'jwks_url', 'jwks_pairs' or 'oidc_discovery_url' must be set"), nil case config.OIDCClientID != "" && config.OIDCClientSecret == "", config.OIDCClientID == "" && config.OIDCClientSecret != "": @@ -279,30 +290,32 @@ func (b *jwtAuthBackend) pathConfigWrite(ctx context.Context, req *logical.Reque case config.OIDCClientID != "" && config.OIDCDiscoveryURL == "": return logical.ErrorResponse("'oidc_discovery_url' must be set for OIDC"), nil + case config.JWKSCAPEM != "" && len(config.JWKSPairs) > 0: + return logical.ErrorResponse("CA PEMs must be provided as part of the 'jwks_pairs' when using multiple JWKS URLs"), nil + + case len(config.JWKSPairs) > 0 && config.BoundIssuer != "": + return logical.ErrorResponse("Bound issuer is not supported for use with 'jwks_pairs'"), nil + case config.JWKSURL != "": - keyset, err := jwt.NewJSONWebKeySet(ctx, config.JWKSURL, config.JWKSCAPEM) - if err != nil { - b.Logger().Error("error checking jwks_ca_pem", "error", err) - return logical.ErrorResponse("error checking jwks_ca_pem"), nil + if r := b.validateJWKSURL(ctx, config.JWKSURL, config.JWKSCAPEM); r != nil { + return r, nil } - // Try to verify a correctly formatted JWT. The signature will fail to match, but other - // errors with fetching the remote keyset should be reported. - testJWT := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.Hf3E3iCHzqC5QIQ0nCqS1kw78IiQTRVzsLTuKoDIpdk" - _, err = keyset.VerifySignature(ctx, testJWT) - if err == nil { - err = errors.New("unexpected verification of JWT") + case len(config.JWKSPairs) > 0: + if jwksPairs, err = NewJWKSPairsConfig(config); err != nil { + return logical.ErrorResponse("invalid jwks_pairs: %s", err), nil } - if !strings.Contains(err.Error(), "failed to verify id token signature") { - b.Logger().Error("error checking jwks URL", "error", err) - return logical.ErrorResponse("error checking jwks URL"), nil + for _, p := range jwksPairs { + if r := b.validateJWKSURL(ctx, p.JWKSUrl, p.JWKSCAPEM); r != nil { + return r, nil + } } case len(config.JWTValidationPubKeys) != 0: for _, v := range config.JWTValidationPubKeys { if _, err := certutil.ParsePublicKeyPEM([]byte(v)); err != nil { - return logical.ErrorResponse(errwrap.Wrapf("error parsing public key: {{err}}", err).Error()), nil + return logical.ErrorResponse(fmt.Errorf("error parsing public key: %w", err).Error()), nil } } @@ -403,6 +416,29 @@ func (b *jwtAuthBackend) createCAContext(ctx context.Context, caPEM string) (con return caCtx, nil } +func (b *jwtAuthBackend) validateJWKSURL(ctx context.Context, JWKSURL, JWKSCAPEM string) *logical.Response { + keyset, err := jwt.NewJSONWebKeySet(ctx, JWKSURL, JWKSCAPEM) + if err != nil { + b.Logger().Error("error checking jwks_ca_pem", "error", err) + return logical.ErrorResponse("error checking jwks_ca_pem") + } + + // Try to verify a correctly formatted JWT. The signature will fail to match, but other + // errors with fetching the remote keyset should be reported. + testJWT := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.Hf3E3iCHzqC5QIQ0nCqS1kw78IiQTRVzsLTuKoDIpdk" + _, err = keyset.VerifySignature(ctx, testJWT) + if err == nil { + err = errors.New("unexpected verification of JWT") + } + + if !strings.Contains(err.Error(), "failed to verify id token signature") { + b.Logger().Error("error checking jwks URL", "url", JWKSURL, "error", err) + return logical.ErrorResponse("error checking jwks URL %s", JWKSURL) + } + + return nil +} + type jwtConfig struct { OIDCDiscoveryURL string `json:"oidc_discovery_url"` OIDCDiscoveryCAPEM string `json:"oidc_discovery_ca_pem"` @@ -412,6 +448,7 @@ type jwtConfig struct { OIDCResponseTypes []string `json:"oidc_response_types"` JWKSURL string `json:"jwks_url"` JWKSCAPEM string `json:"jwks_ca_pem"` + JWKSPairs []interface{} `json:"jwks_pairs"` JWTValidationPubKeys []string `json:"jwt_validation_pubkeys"` JWTSupportedAlgs []string `json:"jwt_supported_algs"` BoundIssuer string `json:"bound_issuer"` @@ -425,6 +462,7 @@ type jwtConfig struct { const ( StaticKeys = iota JWKS + MultiJWKS OIDCDiscovery OIDCFlow unconfigured @@ -437,6 +475,8 @@ func (c jwtConfig) authType() int { return StaticKeys case c.JWKSURL != "": return JWKS + case len(c.JWKSPairs) > 0: + return MultiJWKS case c.OIDCDiscoveryURL != "": if c.OIDCClientID != "" && c.OIDCClientSecret != "" { return OIDCFlow diff --git a/path_config_test.go b/path_config_test.go index 474fcc91..32f3059b 100644 --- a/path_config_test.go +++ b/path_config_test.go @@ -30,6 +30,7 @@ func TestConfig_JWT_Read(t *testing.T) { "jwt_supported_algs": []string{}, "jwks_url": "", "jwks_ca_pem": "", + "jwks_pairs": []interface{}{}, "bound_issuer": "http://vault.example.com/", "provider_config": map[string]interface{}{}, "namespace_in_state": false, @@ -139,6 +140,7 @@ func TestConfig_JWT_Write(t *testing.T) { JWTValidationPubKeys: []string{testJWTPubKey}, JWTSupportedAlgs: []string{}, OIDCResponseTypes: []string{}, + JWKSPairs: []interface{}{}, BoundIssuer: "http://vault.example.com/", ProviderConfig: map[string]interface{}{}, NamespaceInState: true, @@ -154,6 +156,84 @@ func TestConfig_JWT_Write(t *testing.T) { } } +func TestConfig_JWKS_Write_Invalid(t *testing.T) { + b, storage := getBackend(t) + + // Create a config with invalid jwks_pairs format + data := map[string]interface{}{ + "jwks_url": "", + "jwks_ca_pem": "", + "jwks_pairs": []interface{}{ + "foo", + "bar", + }, + "oidc_discovery_url": "", + "oidc_discovery_ca_pem": "", + "oidc_client_id": "", + "default_role": "", + "jwt_validation_pubkeys": []string{}, + "jwt_supported_algs": []string{}, + "bound_issuer": "", + } + + req := &logical.Request{ + Operation: logical.UpdateOperation, + Path: configPath, + Storage: storage, + Data: data, + } + + resp, err := b.HandleRequest(context.Background(), req) + if err != nil { + t.Fatal(err) + } + if resp == nil || !resp.IsError() { + t.Fatal("expected error") + } + if !strings.HasPrefix(resp.Error().Error(), "invalid jwks_pairs") { + t.Fatalf("got unexpected error: %v", resp.Error()) + } +} + +func TestConfig_JWKS_Write_BoundIssuer_Invalid(t *testing.T) { + b, storage := getBackend(t) + + // Create a config with invalid jwks_pairs and bound issuer combination + data := map[string]interface{}{ + "jwks_url": "", + "jwks_ca_pem": "", + "jwks_pairs": []interface{}{ + map[string]interface{}{"jwks_url": "https://www.foobar.com/certs", "jwks_ca_pem": "cert"}, + map[string]interface{}{"jwks_url": "https://www.barbaz.com/certs", "jwks_ca_pem": "cert2"}, + }, + "oidc_discovery_url": "", + "oidc_discovery_ca_pem": "", + "oidc_client_id": "", + "default_role": "", + "jwt_validation_pubkeys": []string{}, + "jwt_supported_algs": []string{}, + "bound_issuer": "foobar", + } + + req := &logical.Request{ + Operation: logical.UpdateOperation, + Path: configPath, + Storage: storage, + Data: data, + } + + resp, err := b.HandleRequest(context.Background(), req) + if err != nil { + t.Fatal(err) + } + if resp == nil || !resp.IsError() { + t.Fatal("expected error") + } + if !strings.HasPrefix(resp.Error().Error(), "Bound issuer is not supported for use with 'jwks_pairs'") { + t.Fatalf("got unexpected error: %v", resp.Error()) + } +} + func TestConfig_JWKS_Update(t *testing.T) { b, storage := getBackend(t) @@ -168,6 +248,7 @@ func TestConfig_JWKS_Update(t *testing.T) { data := map[string]interface{}{ "jwks_url": s.server.URL + "/certs", "jwks_ca_pem": cert, + "jwks_pairs": []interface{}{}, "oidc_discovery_url": "", "oidc_discovery_ca_pem": "", "oidc_client_id": "", @@ -224,6 +305,7 @@ func TestConfig_JWKS_Update_Invalid(t *testing.T) { data := map[string]interface{}{ "jwks_url": s.server.URL + "/certs_missing", "jwks_ca_pem": cert, + "jwks_pairs": map[string]interface{}{}, "oidc_discovery_url": "", "oidc_discovery_ca_pem": "", "oidc_client_id": "", @@ -272,6 +354,154 @@ func TestConfig_JWKS_Update_Invalid(t *testing.T) { } } +func TestConfig_JWKS_Pairs_Update(t *testing.T) { + b, storage := getBackend(t) + + s := newOIDCProvider(t) + defer s.server.Close() + + s2 := newOIDCProvider(t) + defer s2.server.Close() + + cert, err := s.getTLSCert() + if err != nil { + t.Fatal(err) + } + + cert2, err := s2.getTLSCert() + if err != nil { + t.Fatal(err) + } + + data := map[string]interface{}{ + "jwks_url": "", + "jwks_ca_pem": "", + "jwks_pairs": []interface{}{ + map[string]interface{}{"jwks_url": s.server.URL + "/certs", "jwks_ca_pem": cert}, + map[string]interface{}{"jwks_url": s2.server.URL + "/certs", "jwks_ca_pem": cert2}, + }, + "oidc_discovery_url": "", + "oidc_discovery_ca_pem": "", + "oidc_client_id": "", + "oidc_response_mode": "form_post", + "oidc_response_types": []string{}, + "default_role": "", + "jwt_validation_pubkeys": []string{}, + "jwt_supported_algs": []string{}, + "bound_issuer": "", + "provider_config": map[string]interface{}{}, + "namespace_in_state": false, + } + + req := &logical.Request{ + Operation: logical.UpdateOperation, + Path: configPath, + Storage: storage, + Data: data, + } + + resp, err := b.HandleRequest(context.Background(), req) + if err != nil || (resp != nil && resp.IsError()) { + t.Fatalf("err:%s resp:%#v\n", err, resp) + } + + req = &logical.Request{ + Operation: logical.ReadOperation, + Path: configPath, + Storage: storage, + Data: nil, + } + + resp, err = b.HandleRequest(context.Background(), req) + if err != nil || (resp != nil && resp.IsError()) { + t.Fatalf("err:%s resp:%#v\n", err, resp) + } + + if diff := deep.Equal(resp.Data, data); diff != nil { + t.Fatalf("Expected did not equal actual: %v", diff) + } +} + +func TestConfig_JWKS_Pairs_Update_Invalid(t *testing.T) { + b, storage := getBackend(t) + + s := newOIDCProvider(t) + defer s.server.Close() + + s2 := newOIDCProvider(t) + defer s.server.Close() + + cert, err := s.getTLSCert() + if err != nil { + t.Fatal(err) + } + + cert2, err := s2.getTLSCert() + if err != nil { + t.Fatal(err) + } + + data := map[string]interface{}{ + "jwks_url": "", + "jwks_ca_pem": "", + "jwks_pairs": []interface{}{ + map[string]interface{}{"jwks_url": s.server.URL + "/certs_missing", "jwks_ca_pem": cert}, + map[string]interface{}{"jwks_url": s2.server.URL + "/certs", "jwks_ca_pem": cert2}, + }, + "oidc_discovery_url": "", + "oidc_discovery_ca_pem": "", + "oidc_client_id": "", + "default_role": "", + "jwt_validation_pubkeys": []string{}, + "jwt_supported_algs": []string{}, + "bound_issuer": "", + } + + req := &logical.Request{ + Operation: logical.UpdateOperation, + Path: configPath, + Storage: storage, + Data: data, + } + + resp, err := b.HandleRequest(context.Background(), req) + if err != nil { + t.Fatal(err) + } + if resp == nil || !resp.IsError() { + t.Fatal("expected error") + } + if !strings.Contains(resp.Error().Error(), "error checking jwks URL") { + t.Fatalf("got unexpected error: %v", resp.Error()) + } + + // remove the /certs_missing url from the config + newPairs := []interface{}{ + map[string]interface{}{"jwks_url": s.server.URL + "/certs_invalid", "jwks_ca_pem": cert}, + map[string]interface{}{"jwks_url": s2.server.URL + "/certs", "jwks_ca_pem": cert2}, + } + + data["jwks_pairs"] = newPairs + + req = &logical.Request{ + Operation: logical.UpdateOperation, + Path: configPath, + Storage: storage, + Data: data, + } + + resp, err = b.HandleRequest(context.Background(), req) + if err != nil { + t.Fatal(err) + } + if resp == nil || !resp.IsError() { + t.Fatal("expected error") + } + if !strings.Contains(resp.Error().Error(), "error checking jwks URL") { + t.Fatalf("got unexpected error: %v", resp.Error()) + } +} + func TestConfig_ResponseMode(t *testing.T) { b, storage := getBackend(t) @@ -348,6 +578,7 @@ func TestConfig_OIDC_Write(t *testing.T) { expected := &jwtConfig{ JWTValidationPubKeys: []string{}, JWTSupportedAlgs: []string{}, + JWKSPairs: []interface{}{}, OIDCResponseTypes: []string{}, OIDCDiscoveryURL: "https://team-vault.auth0.com/", OIDCClientID: "abc", @@ -439,6 +670,7 @@ func TestConfig_OIDC_Write_ProviderConfig(t *testing.T) { expected := &jwtConfig{ JWTValidationPubKeys: []string{}, JWTSupportedAlgs: []string{}, + JWKSPairs: []interface{}{}, OIDCResponseTypes: []string{}, OIDCDiscoveryURL: "https://team-vault.auth0.com/", ProviderConfig: map[string]interface{}{ @@ -499,6 +731,7 @@ func TestConfig_OIDC_Write_ProviderConfig(t *testing.T) { expected := &jwtConfig{ JWTValidationPubKeys: []string{}, JWTSupportedAlgs: []string{}, + JWKSPairs: []interface{}{}, OIDCResponseTypes: []string{}, OIDCDiscoveryURL: "https://team-vault.auth0.com/", ProviderConfig: map[string]interface{}{}, @@ -530,6 +763,7 @@ func TestConfig_OIDC_Create_Namespace(t *testing.T) { OIDCDiscoveryURL: "https://team-vault.auth0.com/", NamespaceInState: true, OIDCResponseTypes: []string{}, + JWKSPairs: []interface{}{}, JWTSupportedAlgs: []string{}, JWTValidationPubKeys: []string{}, ProviderConfig: map[string]interface{}{}, @@ -544,6 +778,7 @@ func TestConfig_OIDC_Create_Namespace(t *testing.T) { OIDCDiscoveryURL: "https://team-vault.auth0.com/", NamespaceInState: true, OIDCResponseTypes: []string{}, + JWKSPairs: []interface{}{}, JWTSupportedAlgs: []string{}, JWTValidationPubKeys: []string{}, ProviderConfig: map[string]interface{}{}, @@ -558,6 +793,7 @@ func TestConfig_OIDC_Create_Namespace(t *testing.T) { OIDCDiscoveryURL: "https://team-vault.auth0.com/", NamespaceInState: false, OIDCResponseTypes: []string{}, + JWKSPairs: []interface{}{}, JWTSupportedAlgs: []string{}, JWTValidationPubKeys: []string{}, ProviderConfig: map[string]interface{}{}, @@ -606,6 +842,7 @@ func TestConfig_OIDC_Update_Namespace(t *testing.T) { OIDCDiscoveryURL: "https://team-vault.auth0.com/", NamespaceInState: true, OIDCResponseTypes: []string{}, + JWKSPairs: []interface{}{}, JWTSupportedAlgs: []string{}, JWTValidationPubKeys: []string{}, ProviderConfig: map[string]interface{}{}, @@ -625,6 +862,7 @@ func TestConfig_OIDC_Update_Namespace(t *testing.T) { NamespaceInState: false, DefaultRole: "ui", OIDCResponseTypes: []string{}, + JWKSPairs: []interface{}{}, JWTSupportedAlgs: []string{}, JWTValidationPubKeys: []string{}, ProviderConfig: map[string]interface{}{}, @@ -643,6 +881,7 @@ func TestConfig_OIDC_Update_Namespace(t *testing.T) { OIDCDiscoveryURL: "https://team-vault.auth0.com/", NamespaceInState: false, OIDCResponseTypes: []string{}, + JWKSPairs: []interface{}{}, JWTSupportedAlgs: []string{}, JWTValidationPubKeys: []string{}, ProviderConfig: map[string]interface{}{}, @@ -662,6 +901,7 @@ func TestConfig_OIDC_Update_Namespace(t *testing.T) { NamespaceInState: true, DefaultRole: "ui", OIDCResponseTypes: []string{}, + JWKSPairs: []interface{}{}, JWTSupportedAlgs: []string{}, JWTValidationPubKeys: []string{}, ProviderConfig: map[string]interface{}{},