git.iko.soy/typeable/oauth2-proxy
on master help sign in
Merge pull request #1559 from oauth2-proxy/refactor-oidc-verifier

Introduce ProviderVerifier to clean up OIDC discovery code

commitb547fe0b3726d1acf58dd25c2db79dcd805d35cf authorJoel Speed <Joel.speed@hotmail.co.uk> dateSat 19 Feb 2022, 15:43 (5 years ago) parent1578d90c3158eb
21 files changed+695−212
CHANGELOG.md+1 go.mod+3−1 go.sum+10−2 oauthproxy_test.go+2−2 pkg/apis/options/options.go+12−12 pkg/oidc/oidc_suite_test.go → pkg/providers/oidc/oidc_suite_test.go pkg/providers/oidc/provider.go+98 pkg/providers/oidc/provider_test.go+153 pkg/providers/oidc/provider_verifier.go+155 pkg/providers/oidc/provider_verifier_test.go+173 pkg/oidc/verify.go → pkg/providers/oidc/verifier.go+25−16 pkg/oidc/verify_test.go → pkg/providers/oidc/verifier_test.go+12−12 pkg/validation/options.go+18−25 providers/adfs_test.go+2−2 providers/azure_test.go+2−2 providers/keycloak_oidc_test.go+2−2 providers/oidc_test.go+2−2 providers/provider_data.go+2−2 providers/provider_data_test.go+4−4 providers/providers.go+18−127 providers/providers_test.go+1−1
CHANGELOG.md+1
@@ -8,6 +8,7 @@
88
99## Changes since v7.2.1
1010
11+- [#1559](https://github.com/oauth2-proxy/oauth2-proxy/pull/1559) Introduce ProviderVerifier to clean up OIDC discovery code (@JoelSpeed)
1112- [#1561](https://github.com/oauth2-proxy/oauth2-proxy/pull/1561) Add ppc64le support (@mgiessing)
1213- [#1563](https://github.com/oauth2-proxy/oauth2-proxy/pull/1563) Ensure claim extractor does not attempt profile call when URL is empty (@JoelSpeed)
1314- [#1560](https://github.com/oauth2-proxy/oauth2-proxy/pull/1560) Fix provider data initialisation (@JoelSpeed)
go.mod+3−1
@@ -9,6 +9,7 @@ require (
99 github.com/bitly/go-simplejson v0.5.0
1010 github.com/bsm/redislock v0.7.0
1111 github.com/coreos/go-oidc/v3 v3.0.0
12+ github.com/dgrijalva/jwt-go v3.2.0+incompatible
1213 github.com/fsnotify/fsnotify v1.4.9
1314 github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32
1415 github.com/go-redis/redis/v8 v8.2.3
@@ -18,6 +19,7 @@ require (
1819 github.com/justinas/alice v1.2.0
1920 github.com/mbland/hmacauth v0.0.0-20170912233209-44256dfd4bfa
2021 github.com/mitchellh/mapstructure v1.1.2
22+ github.com/oauth2-proxy/mockoidc v0.0.0-20210703044157-382d3faf2671
2123 github.com/oauth2-proxy/tools/reference-gen v0.0.0-20210118095127-56ffd7384404
2224 github.com/onsi/ginkgo v1.14.1
2325 github.com/onsi/gomega v1.10.2
@@ -26,7 +28,7 @@ require (
2628 github.com/spf13/cast v1.3.0
2729 github.com/spf13/pflag v1.0.5
2830 github.com/spf13/viper v1.6.3
29− github.com/stretchr/testify v1.6.1
31+ github.com/stretchr/testify v1.7.0
3032 github.com/vmihailenco/msgpack/v4 v4.3.11
3133 golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97
3234 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110
go.sum+10−2
@@ -81,6 +81,7 @@ github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7Do
8181github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
8282github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
8383github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
84+github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
8485github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
8586github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
8687github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
@@ -169,8 +170,9 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a
169170github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
170171github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
171172github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
172−github.com/google/go-cmp v0.5.1 h1:JFrFEBb2xKufg6XkJsJr+WbKb4FQlURi5RUcBveYu9k=
173173github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
174+github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M=
175+github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
174176github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
175177github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
176178github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
@@ -293,6 +295,8 @@ github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxzi
293295github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
294296github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
295297github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
298+github.com/oauth2-proxy/mockoidc v0.0.0-20210703044157-382d3faf2671 h1:rCw45DEDqAy46HsVu2WZQgEqKth79c8k6HZP2uPDTls=
299+github.com/oauth2-proxy/mockoidc v0.0.0-20210703044157-382d3faf2671/go.mod h1:ejAP84CdQJiV7GcnIF0wNhbhcWsxew0qXV04CB+r+Mw=
296300github.com/oauth2-proxy/tools/reference-gen v0.0.0-20210118095127-56ffd7384404 h1:ZpzR4Ou1nhldBG/vEzauoqyaUlofaUcLkv1C/gBK8ls=
297301github.com/oauth2-proxy/tools/reference-gen v0.0.0-20210118095127-56ffd7384404/go.mod h1:YpORG8zs14vNlpXvuHYnnDvWazIRaDk02MaY8lafqdI=
298302github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
@@ -412,8 +416,9 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
412416github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
413417github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
414418github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
415−github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
416419github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
420+github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
421+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
417422github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
418423github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
419424github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
@@ -456,6 +461,7 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U
456461golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
457462golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
458463golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
464+golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
459465golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI=
460466golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
461467golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -535,6 +541,7 @@ golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7w
535541golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
536542golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
537543golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
544+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
538545golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
539546golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
540547golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -547,6 +554,7 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
547554golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
548555golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4=
549556golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
557+golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
550558golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
551559golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
552560golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
oauthproxy_test.go+2−2
@@ -21,7 +21,7 @@ import (
2121 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
2222 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/cookies"
2323 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger"
24− internaloidc "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/oidc"
24+ internaloidc "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/providers/oidc"
2525 sessionscookie "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/sessions/cookie"
2626 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/upstream"
2727 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/validation"
@@ -1747,7 +1747,7 @@ func TestGetJwtSession(t *testing.T) {
17471747 verifier := oidc.NewVerifier("https://issuer.example.com", keyset,
17481748 &oidc.Config{ClientID: "https://test.myapp.com", SkipExpiryCheck: true,
17491749 SkipClientIDCheck: true})
1750− verificationOptions := &internaloidc.IDTokenVerificationOptions{
1750+ verificationOptions := internaloidc.IDTokenVerificationOptions{
17511751 AudienceClaims: []string{"aud"},
17521752 ClientID: "https://test.myapp.com",
17531753 ExtraAudiences: []string{},
pkg/apis/options/options.go+12−12
@@ -5,7 +5,7 @@ import (
55 "net/url"
66
77 ipapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/ip"
8− internaloidc "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/oidc"
8+ internaloidc "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/providers/oidc"
99 "github.com/spf13/pflag"
1010)
1111
@@ -68,26 +68,26 @@ type Options struct {
6868 // internal values that are set after config validation
6969 redirectURL *url.URL
7070 signatureData *SignatureData
71− oidcVerifier *internaloidc.IDTokenVerifier
72− jwtBearerVerifiers []*internaloidc.IDTokenVerifier
71+ oidcVerifier internaloidc.IDTokenVerifier
72+ jwtBearerVerifiers []internaloidc.IDTokenVerifier
7373 realClientIPParser ipapi.RealClientIPParser
7474}
7575
7676// Options for Getting internal values
77−func (o *Options) GetRedirectURL() *url.URL { return o.redirectURL }
78−func (o *Options) GetSignatureData() *SignatureData { return o.signatureData }
79−func (o *Options) GetOIDCVerifier() *internaloidc.IDTokenVerifier { return o.oidcVerifier }
80−func (o *Options) GetJWTBearerVerifiers() []*internaloidc.IDTokenVerifier {
77+func (o *Options) GetRedirectURL() *url.URL { return o.redirectURL }
78+func (o *Options) GetSignatureData() *SignatureData { return o.signatureData }
79+func (o *Options) GetOIDCVerifier() internaloidc.IDTokenVerifier { return o.oidcVerifier }
80+func (o *Options) GetJWTBearerVerifiers() []internaloidc.IDTokenVerifier {
8181 return o.jwtBearerVerifiers
8282}
8383func (o *Options) GetRealClientIPParser() ipapi.RealClientIPParser { return o.realClientIPParser }
8484
8585// Options for Setting internal values
86−func (o *Options) SetRedirectURL(s *url.URL) { o.redirectURL = s }
87−func (o *Options) SetSignatureData(s *SignatureData) { o.signatureData = s }
88−func (o *Options) SetOIDCVerifier(s *internaloidc.IDTokenVerifier) { o.oidcVerifier = s }
89−func (o *Options) SetJWTBearerVerifiers(s []*internaloidc.IDTokenVerifier) { o.jwtBearerVerifiers = s }
90−func (o *Options) SetRealClientIPParser(s ipapi.RealClientIPParser) { o.realClientIPParser = s }
86+func (o *Options) SetRedirectURL(s *url.URL) { o.redirectURL = s }
87+func (o *Options) SetSignatureData(s *SignatureData) { o.signatureData = s }
88+func (o *Options) SetOIDCVerifier(s internaloidc.IDTokenVerifier) { o.oidcVerifier = s }
89+func (o *Options) SetJWTBearerVerifiers(s []internaloidc.IDTokenVerifier) { o.jwtBearerVerifiers = s }
90+func (o *Options) SetRealClientIPParser(s ipapi.RealClientIPParser) { o.realClientIPParser = s }
9191
9292// NewOptions constructs a new Options with defaulted values
9393func NewOptions() *Options {
pkg/oidc/oidc_suite_test.go → pkg/providers/oidc/oidc_suite_test.gorenamed

The content is the same.

pkg/providers/oidc/provider.gonew file+98
@@ -0,0 +1,98 @@
1+package oidc
2+
3+import (
4+ "context"
5+ "fmt"
6+ "strings"
7+
8+ "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger"
9+ "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/requests"
10+)
11+
12+// providerJSON resresents the information we need from an OIDC discovery
13+type providerJSON struct {
14+ Issuer string `json:"issuer"`
15+ AuthURL string `json:"authorization_endpoint"`
16+ TokenURL string `json:"token_endpoint"`
17+ JWKsURL string `json:"jwks_uri"`
18+ UserInfoURL string `json:"userinfo_endpoint"`
19+ CodeChallengeAlgs []string `json:"code_challenge_methods_supported"`
20+}
21+
22+// Endpoints represents the endpoints discovered as part of the OIDC discovery process
23+// that will be used by the authentication providers.
24+type Endpoints struct {
25+ AuthURL string
26+ TokenURL string
27+ JWKsURL string
28+ UserInfoURL string
29+}
30+
31+// PKCE holds information relevant to the PKCE (code challenge) support of the
32+// provider.
33+type PKCE struct {
34+ CodeChallengeAlgs []string
35+}
36+
37+// DiscoveryProvider holds information about an identity provider having
38+// used OIDC discovery to retrieve the information.
39+type DiscoveryProvider interface {
40+ Endpoints() Endpoints
41+ PKCE() PKCE
42+}
43+
44+// NewProvider allows a user to perform an OIDC discovery and returns the DiscoveryProvider.
45+// We implement this here as opposed to using oidc.Provider so that we can override the Issuer verification check.
46+// As we have our own verifier and fetch the userinfo separately, the rest of the oidc.Provider implementation is not
47+// useful to us.
48+func NewProvider(ctx context.Context, issuerURL string, skipIssuerVerification bool) (DiscoveryProvider, error) {
49+ // go-oidc doesn't let us pass bypass the issuer check this in the oidc.NewProvider call
50+ // (which uses discovery to get the URLs), so we'll do a quick check ourselves and if
51+ // we get the URLs, we'll just use the non-discovery path.
52+
53+ logger.Printf("Performing OIDC Discovery...")
54+
55+ var p providerJSON
56+ requestURL := strings.TrimSuffix(issuerURL, "/") + "/.well-known/openid-configuration"
57+ if err := requests.New(requestURL).WithContext(ctx).Do().UnmarshalInto(&p); err != nil {
58+ return nil, fmt.Errorf("failed to discover OIDC configuration: %v", err)
59+ }
60+
61+ if !skipIssuerVerification && p.Issuer != issuerURL {
62+ return nil, fmt.Errorf("oidc: issuer did not match the issuer returned by provider, expected %q got %q", issuerURL, p.Issuer)
63+ }
64+
65+ return &discoveryProvider{
66+ authURL: p.AuthURL,
67+ tokenURL: p.TokenURL,
68+ jwksURL: p.JWKsURL,
69+ userInfoURL: p.UserInfoURL,
70+ codeChallengeAlgs: p.CodeChallengeAlgs,
71+ }, nil
72+}
73+
74+// discoveryProvider holds the discovered endpoints
75+type discoveryProvider struct {
76+ authURL string
77+ tokenURL string
78+ jwksURL string
79+ userInfoURL string
80+ codeChallengeAlgs []string
81+}
82+
83+// Endpoints returns the discovered endpoints needed for an authentication provider.
84+func (p *discoveryProvider) Endpoints() Endpoints {
85+ return Endpoints{
86+ AuthURL: p.authURL,
87+ TokenURL: p.tokenURL,
88+ JWKsURL: p.jwksURL,
89+ UserInfoURL: p.userInfoURL,
90+ }
91+}
92+
93+// PKCE returns information related to the PKCE (code challenge) support of the provider.
94+func (p *discoveryProvider) PKCE() PKCE {
95+ return PKCE{
96+ CodeChallengeAlgs: p.codeChallengeAlgs,
97+ }
98+}
pkg/providers/oidc/provider_test.gonew file+153
@@ -0,0 +1,153 @@
1+package oidc
2+
3+import (
4+ "context"
5+ "encoding/json"
6+ "net"
7+ "net/http"
8+
9+ "github.com/oauth2-proxy/mockoidc"
10+ . "github.com/onsi/ginkgo"
11+ . "github.com/onsi/ginkgo/extensions/table"
12+ . "github.com/onsi/gomega"
13+)
14+
15+var _ = Describe("Provider", func() {
16+ type newProviderTableInput struct {
17+ skipIssuerVerification bool
18+ expectedError string
19+ middlewares func(*mockoidc.MockOIDC) []func(http.Handler) http.Handler
20+ }
21+
22+ DescribeTable("NewProvider", func(in *newProviderTableInput) {
23+ m, err := mockoidc.NewServer(nil)
24+ Expect(err).ToNot(HaveOccurred())
25+
26+ if in.middlewares != nil {
27+ middlewares := in.middlewares(m)
28+ for _, middlware := range middlewares {
29+ m.AddMiddleware(middlware)
30+ }
31+ }
32+
33+ ln, err := net.Listen("tcp", "127.0.0.1:0")
34+ Expect(err).ToNot(HaveOccurred())
35+
36+ Expect(m.Start(ln, nil)).To(Succeed())
37+ defer func() {
38+ Expect(m.Shutdown()).To(Succeed())
39+ }()
40+
41+ provider, err := NewProvider(context.Background(), m.Issuer(), in.skipIssuerVerification)
42+ if in.expectedError != "" {
43+ Expect(err).To(MatchError(HavePrefix(in.expectedError)))
44+ return
45+ }
46+ Expect(err).ToNot(HaveOccurred())
47+
48+ endpoints := provider.Endpoints()
49+ Expect(endpoints.AuthURL).To(Equal(m.AuthorizationEndpoint()))
50+ Expect(endpoints.TokenURL).To(Equal(m.TokenEndpoint()))
51+ Expect(endpoints.JWKsURL).To(Equal(m.JWKSEndpoint()))
52+ Expect(endpoints.UserInfoURL).To(Equal(m.UserinfoEndpoint()))
53+ },
54+ Entry("with issuer verification and the issuer matches", &newProviderTableInput{
55+ skipIssuerVerification: false,
56+ }),
57+ Entry("with skip issuer verification and the issuer matches", &newProviderTableInput{
58+ skipIssuerVerification: true,
59+ }),
60+ Entry("with issuer verification and an invalid issuer", &newProviderTableInput{
61+ skipIssuerVerification: false,
62+ middlewares: func(m *mockoidc.MockOIDC) []func(http.Handler) http.Handler {
63+ return []func(http.Handler) http.Handler{
64+ newInvalidIssuerMiddleware(m),
65+ }
66+ },
67+ expectedError: "oidc: issuer did not match the issuer returned by provider",
68+ }),
69+ Entry("with skip issuer verification and an invalid issuer", &newProviderTableInput{
70+ skipIssuerVerification: true,
71+ middlewares: func(m *mockoidc.MockOIDC) []func(http.Handler) http.Handler {
72+ return []func(http.Handler) http.Handler{
73+ newInvalidIssuerMiddleware(m),
74+ }
75+ },
76+ }),
77+ Entry("when the issuer returns a bad response", &newProviderTableInput{
78+ skipIssuerVerification: false,
79+ middlewares: func(m *mockoidc.MockOIDC) []func(http.Handler) http.Handler {
80+ return []func(http.Handler) http.Handler{
81+ newBadRequestMiddleware(),
82+ }
83+ },
84+ expectedError: "failed to discover OIDC configuration: unexpected status \"400\"",
85+ }),
86+ )
87+
88+ It("with code challenges supported on the provider, shold populate PKCE information", func() {
89+ m, err := mockoidc.NewServer(nil)
90+ Expect(err).ToNot(HaveOccurred())
91+ m.AddMiddleware(newCodeChallengeIssuerMiddleware(m))
92+
93+ ln, err := net.Listen("tcp", "127.0.0.1:0")
94+ Expect(err).ToNot(HaveOccurred())
95+
96+ Expect(m.Start(ln, nil)).To(Succeed())
97+ defer func() {
98+ Expect(m.Shutdown()).To(Succeed())
99+ }()
100+
101+ provider, err := NewProvider(context.Background(), m.Issuer(), false)
102+ Expect(err).ToNot(HaveOccurred())
103+
104+ Expect(provider.PKCE().CodeChallengeAlgs).To(ConsistOf("S256", "plain"))
105+ })
106+})
107+
108+func newInvalidIssuerMiddleware(m *mockoidc.MockOIDC) func(http.Handler) http.Handler {
109+ return func(next http.Handler) http.Handler {
110+ return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
111+ p := providerJSON{
112+ Issuer: "invalid",
113+ AuthURL: m.AuthorizationEndpoint(),
114+ TokenURL: m.TokenEndpoint(),
115+ JWKsURL: m.JWKSEndpoint(),
116+ UserInfoURL: m.UserinfoEndpoint(),
117+ }
118+ data, err := json.Marshal(p)
119+ if err != nil {
120+ rw.WriteHeader(500)
121+ }
122+ rw.Write(data)
123+ })
124+ }
125+}
126+
127+func newCodeChallengeIssuerMiddleware(m *mockoidc.MockOIDC) func(http.Handler) http.Handler {
128+ return func(next http.Handler) http.Handler {
129+ return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
130+ p := providerJSON{
131+ Issuer: m.Issuer(),
132+ AuthURL: m.AuthorizationEndpoint(),
133+ TokenURL: m.TokenEndpoint(),
134+ JWKsURL: m.JWKSEndpoint(),
135+ UserInfoURL: m.UserinfoEndpoint(),
136+ CodeChallengeAlgs: []string{"S256", "plain"},
137+ }
138+ data, err := json.Marshal(p)
139+ if err != nil {
140+ rw.WriteHeader(500)
141+ }
142+ rw.Write(data)
143+ })
144+ }
145+}
146+
147+func newBadRequestMiddleware() func(http.Handler) http.Handler {
148+ return func(next http.Handler) http.Handler {
149+ return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
150+ rw.WriteHeader(400)
151+ })
152+ }
153+}
pkg/providers/oidc/provider_verifier.gonew file+155
@@ -0,0 +1,155 @@
1+package oidc
2+
3+import (
4+ "context"
5+ "errors"
6+ "fmt"
7+
8+ "github.com/coreos/go-oidc/v3/oidc"
9+ k8serrors "k8s.io/apimachinery/pkg/util/errors"
10+)
11+
12+// ProviderVerifier represents the OIDC discovery and verification process
13+type ProviderVerifier interface {
14+ DiscoveryEnabled() bool
15+ Provider() DiscoveryProvider
16+ Verifier() IDTokenVerifier
17+}
18+
19+// ProviderVerifierOptions allows you to configure a ProviderVerifier
20+type ProviderVerifierOptions struct {
21+ // AudienceClaim allows to define any claim that is verified against the client id
22+ // By default `aud` claim is used for verification.
23+ AudienceClaims []string
24+
25+ // ClientID is the OAuth Client ID that is defined in the provider
26+ ClientID string
27+
28+ // ExtraAudiences is a list of additional audiences that are allowed
29+ // to pass verification in addition to the client id.
30+ ExtraAudiences []string
31+
32+ // IssuerURL is the OpenID Connect issuer URL
33+ // eg: https://accounts.google.com
34+ IssuerURL string
35+
36+ // JWKsURL is the OpenID Connect JWKS URL
37+ // eg: https://www.googleapis.com/oauth2/v3/certs
38+ JWKsURL string
39+
40+ // SkipDiscovery allows to skip OIDC discovery and use manually supplied Endpoints
41+ SkipDiscovery bool
42+
43+ // SkipIssuerVerification skips verification of ID token issuers.
44+ // When false, ID Token Issuers must match the OIDC discovery URL.
45+ SkipIssuerVerification bool
46+}
47+
48+// validate checks that the required options are present before attempting to create
49+// the ProviderVerifier.
50+func (p ProviderVerifierOptions) validate() error {
51+ var errs []error
52+
53+ if p.IssuerURL == "" {
54+ errs = append(errs, errors.New("missing required setting: issuer-url"))
55+ }
56+
57+ if p.SkipDiscovery && p.JWKsURL == "" {
58+ errs = append(errs, errors.New("missing required setting: jwks-url"))
59+ }
60+
61+ if len(errs) > 0 {
62+ return k8serrors.NewAggregate(errs)
63+ }
64+ return nil
65+}
66+
67+// toVerificationOptions returns an IDTokenVerificationOptions based on the configured options.
68+func (p ProviderVerifierOptions) toVerificationOptions() IDTokenVerificationOptions {
69+ return IDTokenVerificationOptions{
70+ AudienceClaims: p.AudienceClaims,
71+ ClientID: p.ClientID,
72+ ExtraAudiences: p.ExtraAudiences,
73+ }
74+}
75+
76+// toOIDCConfig returns an oidc.Config based on the configured options.
77+func (p ProviderVerifierOptions) toOIDCConfig() *oidc.Config {
78+ return &oidc.Config{
79+ ClientID: p.ClientID,
80+ SkipIssuerCheck: p.SkipIssuerVerification,
81+ SkipClientIDCheck: true,
82+ }
83+}
84+
85+// NewProviderVerifier constructs a ProviderVerifier from the options given.
86+func NewProviderVerifier(ctx context.Context, opts ProviderVerifierOptions) (ProviderVerifier, error) {
87+ if err := opts.validate(); err != nil {
88+ return nil, fmt.Errorf("invalid provider verifier options: %v", err)
89+ }
90+
91+ verifierBuilder, provider, err := getVerifierBuilder(ctx, opts)
92+ if err != nil {
93+ return nil, fmt.Errorf("could not get verifier builder: %v", err)
94+ }
95+ verifier := NewVerifier(verifierBuilder(opts.toOIDCConfig()), opts.toVerificationOptions())
96+
97+ if provider == nil {
98+ // To avoid the possibility of nil pointers, always return an empty provider if discovery didn't occur.
99+ // Users are expected to check whether discovery was enabled before using the provider.
100+ provider = &discoveryProvider{}
101+ }
102+
103+ return &providerVerifier{
104+ discoveryEnabled: !opts.SkipDiscovery,
105+ provider: provider,
106+ verifier: verifier,
107+ }, nil
108+}
109+
110+type verifierBuilder func(*oidc.Config) *oidc.IDTokenVerifier
111+
112+func getVerifierBuilder(ctx context.Context, opts ProviderVerifierOptions) (verifierBuilder, DiscoveryProvider, error) {
113+ if opts.SkipDiscovery {
114+ // Instead of discovering the JWKs URK, it needs to be specified in the opts already
115+ return newVerifierBuilder(ctx, opts.IssuerURL, opts.JWKsURL), nil, nil
116+ }
117+
118+ provider, err := NewProvider(ctx, opts.IssuerURL, opts.SkipIssuerVerification)
119+ if err != nil {
120+ return nil, nil, fmt.Errorf("error while discovery OIDC configuration: %v", err)
121+ }
122+ verifierBuilder := newVerifierBuilder(ctx, opts.IssuerURL, provider.Endpoints().JWKsURL)
123+ return verifierBuilder, provider, nil
124+}
125+
126+// newVerifierBuilder returns a function to create a IDToken verifier from an OIDC config.
127+func newVerifierBuilder(ctx context.Context, issuerURL, jwksURL string) verifierBuilder {
128+ keySet := oidc.NewRemoteKeySet(ctx, jwksURL)
129+ return func(oidcConfig *oidc.Config) *oidc.IDTokenVerifier {
130+ return oidc.NewVerifier(issuerURL, keySet, oidcConfig)
131+ }
132+}
133+
134+// providerVerifier is an implementation of the ProviderVerifier interface
135+type providerVerifier struct {
136+ discoveryEnabled bool
137+ provider DiscoveryProvider
138+ verifier IDTokenVerifier
139+}
140+
141+// DiscoveryEnabled returns whether the provider verifier was constructed
142+// using the OIDC discovery process or whether it was manually discovered.
143+func (p *providerVerifier) DiscoveryEnabled() bool {
144+ return p.discoveryEnabled
145+}
146+
147+// Provider returns the OIDC discovery provider
148+func (p *providerVerifier) Provider() DiscoveryProvider {
149+ return p.provider
150+}
151+
152+// Verifier returns the ID token verifier
153+func (p *providerVerifier) Verifier() IDTokenVerifier {
154+ return p.verifier
155+}
pkg/providers/oidc/provider_verifier_test.gonew file+173
@@ -0,0 +1,173 @@
1+package oidc
2+
3+import (
4+ "context"
5+ "time"
6+
7+ "github.com/dgrijalva/jwt-go"
8+ "github.com/oauth2-proxy/mockoidc"
9+ . "github.com/onsi/ginkgo"
10+ . "github.com/onsi/ginkgo/extensions/table"
11+ . "github.com/onsi/gomega"
12+)
13+
14+var _ = Describe("ProviderVerifier", func() {
15+ var m *mockoidc.MockOIDC
16+
17+ BeforeEach(func() {
18+ var err error
19+ m, err = mockoidc.Run()
20+ Expect(err).ToNot(HaveOccurred())
21+ })
22+
23+ AfterEach(func() {
24+ Expect(m.Shutdown()).To(Succeed())
25+ })
26+
27+ type newProviderVerifierTableInput struct {
28+ modifyOpts func(*ProviderVerifierOptions)
29+ expectedError string
30+ }
31+
32+ DescribeTable("when constructing the provider verifier", func(in *newProviderVerifierTableInput) {
33+ opts := ProviderVerifierOptions{
34+ AudienceClaims: []string{"aud"},
35+ ClientID: m.Config().ClientID,
36+ ExtraAudiences: []string{},
37+ IssuerURL: m.Issuer(),
38+ }
39+ if in.modifyOpts != nil {
40+ in.modifyOpts(&opts)
41+ }
42+
43+ pv, err := NewProviderVerifier(context.Background(), opts)
44+ if in.expectedError != "" {
45+ Expect(err).To(MatchError(HavePrefix(in.expectedError)))
46+ return
47+ }
48+ Expect(err).ToNot(HaveOccurred())
49+
50+ Expect(pv.DiscoveryEnabled()).ToNot(Equal(opts.SkipDiscovery), "DiscoveryEnabled should be the reverse of skip discovery")
51+ Expect(pv.Provider()).ToNot(BeNil())
52+
53+ if pv.DiscoveryEnabled() {
54+ endpoints := pv.Provider().Endpoints()
55+ Expect(endpoints.AuthURL).To(Equal(m.AuthorizationEndpoint()))
56+ Expect(endpoints.TokenURL).To(Equal(m.TokenEndpoint()))
57+ Expect(endpoints.JWKsURL).To(Equal(m.JWKSEndpoint()))
58+ Expect(endpoints.UserInfoURL).To(Equal(m.UserinfoEndpoint()))
59+ }
60+ },
61+ Entry("should be succesfful when discovering the OIDC provider", &newProviderVerifierTableInput{
62+ modifyOpts: func(_ *ProviderVerifierOptions) {},
63+ }),
64+ Entry("when the issuer URL is missing", &newProviderVerifierTableInput{
65+ modifyOpts: func(p *ProviderVerifierOptions) {
66+ p.IssuerURL = ""
67+ },
68+ expectedError: "invalid provider verifier options: missing required setting: issuer-url",
69+ }),
70+ Entry("when the issuer URL is invalid", &newProviderVerifierTableInput{
71+ modifyOpts: func(p *ProviderVerifierOptions) {
72+ p.IssuerURL = "invalid"
73+ },
74+ expectedError: "could not get verifier builder: error while discovery OIDC configuration: failed to discover OIDC configuration: error performing request: Get \"invalid/.well-known/openid-configuration\": unsupported protocol scheme \"\"",
75+ }),
76+ Entry("with skip discovery and the JWKs URL is missing", &newProviderVerifierTableInput{
77+ modifyOpts: func(p *ProviderVerifierOptions) {
78+ p.SkipDiscovery = true
79+ p.JWKsURL = ""
80+ },
81+ expectedError: "invalid provider verifier options: missing required setting: jwks-url",
82+ }),
83+ Entry("should be succesfful when skipping discovery with the JWKs URL specified", &newProviderVerifierTableInput{
84+ modifyOpts: func(p *ProviderVerifierOptions) {
85+ p.SkipDiscovery = true
86+ p.JWKsURL = m.JWKSEndpoint()
87+ },
88+ }),
89+ )
90+
91+ type verifierTableInput struct {
92+ modifyOpts func(*ProviderVerifierOptions)
93+ modifyClaims func(*jwt.StandardClaims)
94+ expectedError string
95+ }
96+
97+ DescribeTable("when constructing the provider verifier", func(in *verifierTableInput) {
98+ opts := ProviderVerifierOptions{
99+ AudienceClaims: []string{"aud"},
100+ ClientID: m.Config().ClientID,
101+ ExtraAudiences: []string{},
102+ IssuerURL: m.Issuer(),
103+ }
104+ if in.modifyOpts != nil {
105+ in.modifyOpts(&opts)
106+ }
107+
108+ pv, err := NewProviderVerifier(context.Background(), opts)
109+ Expect(err).ToNot(HaveOccurred())
110+
111+ now := time.Now()
112+ claims := jwt.StandardClaims{
113+ Audience: m.Config().ClientID,
114+ Issuer: m.Issuer(),
115+ ExpiresAt: now.Add(1 * time.Hour).Unix(),
116+ IssuedAt: now.Unix(),
117+ Subject: "user",
118+ }
119+ if in.modifyClaims != nil {
120+ in.modifyClaims(&claims)
121+ }
122+
123+ rawIDToken, err := m.Keypair.SignJWT(claims)
124+ Expect(err).ToNot(HaveOccurred())
125+
126+ idToken, err := pv.Verifier().Verify(context.Background(), rawIDToken)
127+ if in.expectedError != "" {
128+ Expect(err).To(MatchError(HavePrefix(in.expectedError)))
129+ return
130+ }
131+ Expect(err).ToNot(HaveOccurred())
132+
133+ Expect(idToken.Issuer).To(Equal(claims.Issuer))
134+ Expect(idToken.Audience).To(ConsistOf(claims.Audience))
135+ Expect(idToken.Subject).To(Equal(claims.Subject))
136+ },
137+ Entry("with the default opts and claims", &verifierTableInput{}),
138+ Entry("when the audience is mismatched", &verifierTableInput{
139+ modifyClaims: func(j *jwt.StandardClaims) {
140+ j.Audience = "OtherClient"
141+ },
142+ expectedError: "audience from claim aud with value [OtherClient] does not match with any of allowed audiences",
143+ }),
144+ Entry("when the audience is an extra audience", &verifierTableInput{
145+ modifyOpts: func(p *ProviderVerifierOptions) {
146+ p.ExtraAudiences = []string{"ExtraIssuer"}
147+ },
148+ modifyClaims: func(j *jwt.StandardClaims) {
149+ j.Audience = "ExtraIssuer"
150+ },
151+ }),
152+ Entry("when the issuer is mismatched", &verifierTableInput{
153+ modifyClaims: func(j *jwt.StandardClaims) {
154+ j.Issuer = "OtherIssuer"
155+ },
156+ expectedError: "failed to verify token: oidc: id token issued by a different provider",
157+ }),
158+ Entry("when the issuer is mismatched with skip issuer verification", &verifierTableInput{
159+ modifyOpts: func(p *ProviderVerifierOptions) {
160+ p.SkipIssuerVerification = true
161+ },
162+ modifyClaims: func(j *jwt.StandardClaims) {
163+ j.Issuer = "OtherIssuer"
164+ },
165+ }),
166+ Entry("when the token has expired", &verifierTableInput{
167+ modifyClaims: func(j *jwt.StandardClaims) {
168+ j.ExpiresAt = time.Now().Add(-1 * time.Hour).Unix()
169+ },
170+ expectedError: "failed to verify token: oidc: token is expired",
171+ }),
172+ )
173+})
pkg/oidc/verify.go → pkg/providers/oidc/verifier.gorenamed+25−16
@@ -8,33 +8,42 @@ import (
88 "github.com/coreos/go-oidc/v3/oidc"
99)
1010
11−// IDTokenVerifier Used to verify an ID Token and extends oidc.IDTokenVerifier from the underlying oidc library
12−type IDTokenVerifier struct {
13− *oidc.IDTokenVerifier
14− *IDTokenVerificationOptions
15− allowedAudiences map[string]struct{}
11+// idTokenVerifier allows an ID Token to be verified against the issue and provided keys.
12+type IDTokenVerifier interface {
13+ Verify(context.Context, string) (*oidc.IDToken, error)
1614}
1715
18−// IDTokenVerificationOptions options for the oidc.IDTokenVerifier that are required to verify an ID Token
16+// idTokenVerifier Used to verify an ID Token and extends oidc.idTokenVerifier from the underlying oidc library
17+type idTokenVerifier struct {
18+ verifier *oidc.IDTokenVerifier
19+ verificationOptions IDTokenVerificationOptions
20+ allowedAudiences map[string]struct{}
21+}
22+
23+// IDTokenVerificationOptions options for the oidc.idTokenVerifier that are required to verify an ID Token
1924type IDTokenVerificationOptions struct {
2025 AudienceClaims []string
2126 ClientID string
2227 ExtraAudiences []string
2328}
2429
25−// NewVerifier constructs a new IDTokenVerifier
26−func NewVerifier(iv *oidc.IDTokenVerifier, vo *IDTokenVerificationOptions) *IDTokenVerifier {
30+// NewVerifier constructs a new idTokenVerifier
31+func NewVerifier(iv *oidc.IDTokenVerifier, vo IDTokenVerificationOptions) IDTokenVerifier {
2732 allowedAudiences := make(map[string]struct{})
2833 allowedAudiences[vo.ClientID] = struct{}{}
2934 for _, extraAudience := range vo.ExtraAudiences {
3035 allowedAudiences[extraAudience] = struct{}{}
3136 }
32− return &IDTokenVerifier{iv, vo, allowedAudiences}
37+ return &idTokenVerifier{
38+ verifier: iv,
39+ verificationOptions: vo,
40+ allowedAudiences: allowedAudiences,
41+ }
3342}
3443
3544// Verify verifies incoming ID Token
36−func (v *IDTokenVerifier) Verify(ctx context.Context, rawIDToken string) (*oidc.IDToken, error) {
37− token, err := v.IDTokenVerifier.Verify(ctx, rawIDToken)
45+func (v *idTokenVerifier) Verify(ctx context.Context, rawIDToken string) (*oidc.IDToken, error) {
46+ token, err := v.verifier.Verify(ctx, rawIDToken)
3847 if err != nil {
3948 return nil, fmt.Errorf("failed to verify token: %v", err)
4049 }
@@ -51,8 +60,8 @@ func (v *IDTokenVerifier) Verify(ctx context.Context, rawIDToken string) (*oidc.
5160 return token, err
5261}
5362
54−func (v *IDTokenVerifier) verifyAudience(token *oidc.IDToken, claims map[string]interface{}) (bool, error) {
55− for _, audienceClaim := range v.AudienceClaims {
63+func (v *idTokenVerifier) verifyAudience(token *oidc.IDToken, claims map[string]interface{}) (bool, error) {
64+ for _, audienceClaim := range v.verificationOptions.AudienceClaims {
5665 if audienceClaimValue, audienceClaimExists := claims[audienceClaim]; audienceClaimExists {
5766
5867 // audience claim value can be either interface{} or []interface{},
@@ -72,10 +81,10 @@ func (v *IDTokenVerifier) verifyAudience(token *oidc.IDToken, claims map[string]
7281 }
7382
7483 return false, fmt.Errorf("audience claims %v do not exist in claims: %v",
75− v.AudienceClaims, claims)
84+ v.verificationOptions.AudienceClaims, claims)
7685}
7786
78−func (v *IDTokenVerifier) isValidAudience(claim string, audience []string, allowedAudiences map[string]struct{}) (bool, error) {
87+func (v *idTokenVerifier) isValidAudience(claim string, audience []string, allowedAudiences map[string]struct{}) (bool, error) {
7988 for _, aud := range audience {
8089 if _, allowedAudienceExists := allowedAudiences[aud]; allowedAudienceExists {
8190 return true, nil
@@ -87,7 +96,7 @@ func (v *IDTokenVerifier) isValidAudience(claim string, audience []string, allow
8796 claim, audience, allowedAudiences)
8897}
8998
90−func (v *IDTokenVerifier) interfaceSliceToString(slice interface{}) []string {
99+func (v *idTokenVerifier) interfaceSliceToString(slice interface{}) []string {
91100 s := reflect.ValueOf(slice)
92101 if s.Kind() != reflect.Slice {
93102 panic(fmt.Sprintf("given a non-slice type %s", s.Kind()))
pkg/oidc/verify_test.go → pkg/providers/oidc/verifier_test.gorenamed+12−12
@@ -17,7 +17,7 @@ var _ = Describe("Verify", func() {
1717 ctx := context.Background()
1818
1919 It("Succeeds with default aud behavior", func() {
20− result, err := verify(ctx, &IDTokenVerificationOptions{
20+ result, err := verify(ctx, IDTokenVerificationOptions{
2121 AudienceClaims: []string{"aud"},
2222 ClientID: "1226737",
2323 ExtraAudiences: []string{},
@@ -32,7 +32,7 @@ var _ = Describe("Verify", func() {
3232 })
3333
3434 It("Fails with default aud behavior", func() {
35− result, err := verify(ctx, &IDTokenVerificationOptions{
35+ result, err := verify(ctx, IDTokenVerificationOptions{
3636 AudienceClaims: []string{"aud"},
3737 ClientID: "7817818",
3838 ExtraAudiences: []string{},
@@ -46,7 +46,7 @@ var _ = Describe("Verify", func() {
4646 })
4747
4848 It("Succeeds with extra audiences", func() {
49− result, err := verify(ctx, &IDTokenVerificationOptions{
49+ result, err := verify(ctx, IDTokenVerificationOptions{
5050 AudienceClaims: []string{"aud"},
5151 ClientID: "7817818",
5252 ExtraAudiences: []string{"xyz", "1226737"},
@@ -61,7 +61,7 @@ var _ = Describe("Verify", func() {
6161 })
6262
6363 It("Fails with extra audiences", func() {
64− result, err := verify(ctx, &IDTokenVerificationOptions{
64+ result, err := verify(ctx, IDTokenVerificationOptions{
6565 AudienceClaims: []string{"aud"},
6666 ClientID: "7817818",
6767 ExtraAudiences: []string{"xyz", "abc"},
@@ -76,7 +76,7 @@ var _ = Describe("Verify", func() {
7676 })
7777
7878 It("Succeeds with non default aud behavior", func() {
79− result, err := verify(ctx, &IDTokenVerificationOptions{
79+ result, err := verify(ctx, IDTokenVerificationOptions{
8080 AudienceClaims: []string{"client_id"},
8181 ClientID: "1226737",
8282 ExtraAudiences: []string{},
@@ -91,7 +91,7 @@ var _ = Describe("Verify", func() {
9191 })
9292
9393 It("Fails with non default aud behavior", func() {
94− result, err := verify(ctx, &IDTokenVerificationOptions{
94+ result, err := verify(ctx, IDTokenVerificationOptions{
9595 AudienceClaims: []string{"client_id"},
9696 ClientID: "7817818",
9797 ExtraAudiences: []string{},
@@ -105,7 +105,7 @@ var _ = Describe("Verify", func() {
105105 })
106106
107107 It("Succeeds with non default aud behavior and extra audiences", func() {
108− result, err := verify(ctx, &IDTokenVerificationOptions{
108+ result, err := verify(ctx, IDTokenVerificationOptions{
109109 AudienceClaims: []string{"client_id"},
110110 ClientID: "7817818",
111111 ExtraAudiences: []string{"xyz", "1226737"},
@@ -120,7 +120,7 @@ var _ = Describe("Verify", func() {
120120 })
121121
122122 It("Fails with non default aud behavior and extra audiences", func() {
123− result, err := verify(ctx, &IDTokenVerificationOptions{
123+ result, err := verify(ctx, IDTokenVerificationOptions{
124124 AudienceClaims: []string{"client_id"},
125125 ClientID: "7817818",
126126 ExtraAudiences: []string{"xyz", "abc"},
@@ -135,7 +135,7 @@ var _ = Describe("Verify", func() {
135135 })
136136
137137 It("Fails if audience claim does not exist", func() {
138− result, err := verify(ctx, &IDTokenVerificationOptions{
138+ result, err := verify(ctx, IDTokenVerificationOptions{
139139 AudienceClaims: []string{"not_exists"},
140140 ClientID: "7817818",
141141 ExtraAudiences: []string{"xyz", "abc"},
@@ -151,7 +151,7 @@ var _ = Describe("Verify", func() {
151151 })
152152
153153 It("Succeeds with multiple audiences", func() {
154− var result, err = verify(ctx, &IDTokenVerificationOptions{
154+ var result, err = verify(ctx, IDTokenVerificationOptions{
155155 AudienceClaims: []string{"client_id", "aud"},
156156 ClientID: "123456789",
157157 ExtraAudiences: []string{"1226737"},
@@ -167,7 +167,7 @@ var _ = Describe("Verify", func() {
167167 })
168168
169169 It("Succeeds if aud claim match", func() {
170− result, err := verify(ctx, &IDTokenVerificationOptions{
170+ result, err := verify(ctx, IDTokenVerificationOptions{
171171 AudienceClaims: []string{"client_id", "aud"},
172172 ClientID: "1226737",
173173 ExtraAudiences: []string{"xyz", "abc"},
@@ -207,7 +207,7 @@ func (t *testVerifier) VerifySignature(ctx context.Context, jwt string) ([]byte,
207207 return jws.Verify(&t.jwk)
208208}
209209
210−func verify(ctx context.Context, verificationOptions *IDTokenVerificationOptions, payload payload) (*oidc.IDToken, error) {
210+func verify(ctx context.Context, verificationOptions IDTokenVerificationOptions, payload payload) (*oidc.IDToken, error) {
211211 config := &oidc.Config{
212212 ClientID: "1226737",
213213 SkipClientIDCheck: true,
pkg/validation/options.go+18−25
@@ -8,13 +8,11 @@ import (
88 "net/url"
99 "strings"
1010
11− "github.com/coreos/go-oidc/v3/oidc"
1211 "github.com/mbland/hmacauth"
1312 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
1413 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/ip"
1514 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger"
16− internaloidc "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/oidc"
17− "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/requests"
15+ internaloidc "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/providers/oidc"
1816 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/util"
1917)
2018
@@ -148,32 +146,27 @@ func parseJwtIssuers(issuers []string, msgs []string) ([]jwtIssuer, []string) {
148146
149147// newVerifierFromJwtIssuer takes in issuer information in jwtIssuer info and returns
150148// a verifier for that issuer.
151−func newVerifierFromJwtIssuer(audienceClaims []string, extraAudiences []string, jwtIssuer jwtIssuer) (*internaloidc.IDTokenVerifier, error) {
152− config := &oidc.Config{
153− ClientID: jwtIssuer.audience,
154− SkipClientIDCheck: true, // client id check is done within oauth2-proxy: IDTokenVerifier.Verify
155− }
156− // Try as an OpenID Connect Provider first
157− var verifier *oidc.IDTokenVerifier
158− provider, err := oidc.NewProvider(context.Background(), jwtIssuer.issuerURI)
159− if err != nil {
160− // Try as JWKS URI
161− jwksURI := strings.TrimSuffix(jwtIssuer.issuerURI, "/") + "/.well-known/jwks.json"
162− if err := requests.New(jwksURI).Do().Error(); err != nil {
163− return nil, err
164− }
165−
166− verifier = oidc.NewVerifier(jwtIssuer.issuerURI, oidc.NewRemoteKeySet(context.Background(), jwksURI), config)
167− } else {
168− verifier = provider.Verifier(config)
169− }
170− verificationOptions := &internaloidc.IDTokenVerificationOptions{
149+func newVerifierFromJwtIssuer(audienceClaims []string, extraAudiences []string, jwtIssuer jwtIssuer) (internaloidc.IDTokenVerifier, error) {
150+ pvOpts := internaloidc.ProviderVerifierOptions{
171151 AudienceClaims: audienceClaims,
172152 ClientID: jwtIssuer.audience,
173153 ExtraAudiences: extraAudiences,
174− // ExtraAudiences: o.Providers[0].OIDCConfig.ExtraAudiences,
154+ IssuerURL: jwtIssuer.issuerURI,
155+ }
156+
157+ pv, err := internaloidc.NewProviderVerifier(context.TODO(), pvOpts)
158+ if err != nil {
159+ // If the discovery didn't work, try again without discovery
160+ pvOpts.JWKsURL = strings.TrimSuffix(jwtIssuer.issuerURI, "/") + "/.well-known/jwks.json"
161+ pvOpts.SkipDiscovery = true
162+
163+ pv, err = internaloidc.NewProviderVerifier(context.TODO(), pvOpts)
164+ if err != nil {
165+ return nil, fmt.Errorf("could not construct provider verifier for JWT Issuer: %v", err)
166+ }
175167 }
176− return internaloidc.NewVerifier(verifier, verificationOptions), nil
168+
169+ return pv.Verifier(), nil
177170}
178171
179172// jwtIssuer hold parsed JWT issuer info that's used to construct a verifier.
providers/adfs_test.go+2−2
@@ -15,7 +15,7 @@ import (
1515 "github.com/golang-jwt/jwt"
1616 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
1717 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
18− internaloidc "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/oidc"
18+ internaloidc "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/providers/oidc"
1919 . "github.com/onsi/ginkgo"
2020 . "github.com/onsi/ginkgo/extensions/table"
2121 . "github.com/onsi/gomega"
@@ -43,7 +43,7 @@ func newSignedTestADFSToken(tokenClaims adfsClaims) (string, error) {
4343}
4444
4545func testADFSProvider(hostname string) *ADFSProvider {
46− verificationOptions := &internaloidc.IDTokenVerificationOptions{
46+ verificationOptions := internaloidc.IDTokenVerificationOptions{
4747 AudienceClaims: []string{"aud"},
4848 ClientID: "https://test.myapp.com",
4949 }
providers/azure_test.go+2−2
@@ -17,7 +17,7 @@ import (
1717 "github.com/golang-jwt/jwt"
1818 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
1919 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
20− internaloidc "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/oidc"
20+ internaloidc "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/providers/oidc"
2121
2222 . "github.com/onsi/gomega"
2323 "github.com/stretchr/testify/assert"
@@ -41,7 +41,7 @@ type azureOAuthPayload struct {
4141}
4242
4343func testAzureProvider(hostname string, opts options.AzureOptions) *AzureProvider {
44− verificationOptions := &internaloidc.IDTokenVerificationOptions{
44+ verificationOptions := internaloidc.IDTokenVerificationOptions{
4545 AudienceClaims: []string{"aud"},
4646 ClientID: "cd6d4fae-f6a6-4a34-8454-2c6b598e9532",
4747 }
providers/keycloak_oidc_test.go+2−2
@@ -14,7 +14,7 @@ import (
1414 . "github.com/onsi/ginkgo"
1515 . "github.com/onsi/gomega"
1616
17− internaloidc "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/oidc"
17+ internaloidc "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/providers/oidc"
1818)
1919
2020const (
@@ -45,7 +45,7 @@ func newTestKeycloakOIDCSetup() (*httptest.Server, *KeycloakOIDCProvider) {
4545}
4646
4747func newKeycloakOIDCProvider(serverURL *url.URL, opts options.KeycloakOptions) *KeycloakOIDCProvider {
48− verificationOptions := &internaloidc.IDTokenVerificationOptions{
48+ verificationOptions := internaloidc.IDTokenVerificationOptions{
4949 AudienceClaims: []string{defaultAudienceClaim},
5050 ClientID: mockClientID,
5151 }
providers/oidc_test.go+2−2
@@ -14,7 +14,7 @@ import (
1414 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
1515 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
1616 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/encryption"
17− internaloidc "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/oidc"
17+ internaloidc "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/providers/oidc"
1818 "github.com/stretchr/testify/assert"
1919)
2020
@@ -27,7 +27,7 @@ type redeemTokenResponse struct {
2727}
2828
2929func newOIDCProvider(serverURL *url.URL, skipNonce bool) *OIDCProvider {
30− verificationOptions := &internaloidc.IDTokenVerificationOptions{
30+ verificationOptions := internaloidc.IDTokenVerificationOptions{
3131 AudienceClaims: []string{"aud"},
3232 ClientID: "https://test.myapp.com",
3333 }
providers/provider_data.go+2−2
@@ -13,7 +13,7 @@ import (
1313 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
1414 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
1515 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger"
16− internaloidc "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/oidc"
16+ internaloidc "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/providers/oidc"
1717 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/providers/util"
1818 "golang.org/x/oauth2"
1919)
@@ -47,7 +47,7 @@ type ProviderData struct {
4747 UserClaim string
4848 EmailClaim string
4949 GroupsClaim string
50− Verifier *internaloidc.IDTokenVerifier
50+ Verifier internaloidc.IDTokenVerifier
5151
5252 // Universal Group authorization data structure
5353 // any provider can set to consume
providers/provider_data_test.go+4−4
@@ -16,7 +16,7 @@ import (
1616 "github.com/golang-jwt/jwt"
1717 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
1818 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/encryption"
19− internaloidc "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/oidc"
19+ internaloidc "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/providers/oidc"
2020 . "github.com/onsi/gomega"
2121 "golang.org/x/oauth2"
2222)
@@ -202,7 +202,7 @@ func TestProviderData_verifyIDToken(t *testing.T) {
202202
203203 provider := &ProviderData{}
204204 if tc.Verifier {
205− verificationOptions := &internaloidc.IDTokenVerificationOptions{
205+ verificationOptions := internaloidc.IDTokenVerificationOptions{
206206 AudienceClaims: []string{"aud"},
207207 ClientID: oidcClientID,
208208 }
@@ -409,7 +409,7 @@ func TestProviderData_buildSessionFromClaims(t *testing.T) {
409409 t.Run(testName, func(t *testing.T) {
410410 g := NewWithT(t)
411411
412− verificationOptions := &internaloidc.IDTokenVerificationOptions{
412+ verificationOptions := internaloidc.IDTokenVerificationOptions{
413413 AudienceClaims: []string{"aud"},
414414 ClientID: oidcClientID,
415415 }
@@ -478,7 +478,7 @@ func TestProviderData_checkNonce(t *testing.T) {
478478 g.Expect(err).ToNot(HaveOccurred())
479479 tc.Session.IDToken = rawIDToken
480480
481− verificationOptions := &internaloidc.IDTokenVerificationOptions{
481+ verificationOptions := internaloidc.IDTokenVerificationOptions{
482482 AudienceClaims: []string{"aud"},
483483 ClientID: oidcClientID,
484484 }
providers/providers.go+18−127
@@ -2,17 +2,12 @@ package providers
22
33import (
44 "context"
5− "errors"
65 "fmt"
76 "net/url"
8− "strings"
97
10− "github.com/coreos/go-oidc/v3/oidc"
118 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
129 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
13− "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger"
14− internaloidc "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/oidc"
15− "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/requests"
10+ internaloidc "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/providers/oidc"
1611 k8serrors "k8s.io/apimachinery/pkg/util/errors"
1712)
1813
@@ -86,16 +81,27 @@ func newProviderDataFromConfig(providerConfig options.Provider) (*ProviderData,
8681 }
8782
8883 if needsVerifier {
89− oidcProvider, verifier, err := newOIDCProviderVerifier(providerConfig)
84+ pv, err := internaloidc.NewProviderVerifier(context.TODO(), internaloidc.ProviderVerifierOptions{
85+ AudienceClaims: providerConfig.OIDCConfig.AudienceClaims,
86+ ClientID: providerConfig.ClientID,
87+ ExtraAudiences: providerConfig.OIDCConfig.ExtraAudiences,
88+ IssuerURL: providerConfig.OIDCConfig.IssuerURL,
89+ JWKsURL: providerConfig.OIDCConfig.JwksURL,
90+ SkipDiscovery: providerConfig.OIDCConfig.SkipDiscovery,
91+ SkipIssuerVerification: providerConfig.OIDCConfig.InsecureSkipIssuerVerification,
92+ })
9093 if err != nil {
91− return nil, fmt.Errorf("error setting OIDC configuration: %v", err)
94+ return nil, fmt.Errorf("error building OIDC ProviderVerifier: %v", err)
9295 }
9396
94− p.Verifier = verifier
95− if oidcProvider != nil {
97+ p.Verifier = pv.Verifier()
98+ if pv.DiscoveryEnabled() {
9699 // Use the discovered values rather than any specified values
97− providerConfig.LoginURL = oidcProvider.Endpoint().AuthURL
98− providerConfig.RedeemURL = oidcProvider.Endpoint().TokenURL
100+ endpoints := pv.Provider().Endpoints()
101+ providerConfig.LoginURL = endpoints.AuthURL
102+ providerConfig.RedeemURL = endpoints.TokenURL
103+ providerConfig.ProfileURL = endpoints.UserInfoURL
104+ providerConfig.OIDCConfig.JwksURL = endpoints.JWKsURL
99105 }
100106 }
101107
@@ -159,118 +165,3 @@ func providerRequiresOIDCProviderVerifier(providerType options.ProviderType) (bo
159165 return false, fmt.Errorf("unknown provider type: %s", providerType)
160166 }
161167}
162−
163−func newOIDCProviderVerifier(providerConfig options.Provider) (*oidc.Provider, *internaloidc.IDTokenVerifier, error) {
164− // If the issuer isn't set, default it for platforms where it makes sense
165− if providerConfig.OIDCConfig.IssuerURL == "" {
166− switch providerConfig.Type {
167− case "gitlab":
168− providerConfig.OIDCConfig.IssuerURL = "https://gitlab.com"
169− case "oidc":
170− return nil, nil, errors.New("missing required setting: OIDC Issuer URL cannot be empty")
171− }
172− }
173−
174− switch {
175− case providerConfig.OIDCConfig.InsecureSkipIssuerVerification && !providerConfig.OIDCConfig.SkipDiscovery:
176− verifier, err := newInsecureSkipIssuerVerificationOIDCVerifier(providerConfig)
177− return nil, verifier, err
178− case providerConfig.OIDCConfig.SkipDiscovery:
179− verifier, err := newSkipDiscoveryOIDCVerifier(providerConfig)
180− return nil, verifier, err
181− default:
182− return newDiscoveryOIDCProviderVerifier(providerConfig)
183− }
184−}
185−
186−func newDiscoveryOIDCProviderVerifier(providerConfig options.Provider) (*oidc.Provider, *internaloidc.IDTokenVerifier, error) {
187− // Configure discoverable provider data.
188− provider, err := oidc.NewProvider(context.TODO(), providerConfig.OIDCConfig.IssuerURL)
189− if err != nil {
190− return nil, nil, err
191− }
192− verificationOptions := &internaloidc.IDTokenVerificationOptions{
193− AudienceClaims: providerConfig.OIDCConfig.AudienceClaims,
194− ClientID: providerConfig.ClientID,
195− ExtraAudiences: providerConfig.OIDCConfig.ExtraAudiences,
196− }
197− verifier := internaloidc.NewVerifier(provider.Verifier(&oidc.Config{
198− ClientID: providerConfig.ClientID,
199− SkipIssuerCheck: providerConfig.OIDCConfig.InsecureSkipIssuerVerification,
200− SkipClientIDCheck: true, // client id check is done within oauth2-proxy: IDTokenVerifier.Verify
201− }), verificationOptions)
202−
203− return provider, verifier, nil
204−}
205−
206−func newInsecureSkipIssuerVerificationOIDCVerifier(providerConfig options.Provider) (*internaloidc.IDTokenVerifier, error) {
207− // go-oidc doesn't let us pass bypass the issuer check this in the oidc.NewProvider call
208− // (which uses discovery to get the URLs), so we'll do a quick check ourselves and if
209− // we get the URLs, we'll just use the non-discovery path.
210−
211− logger.Printf("Performing OIDC Discovery...")
212−
213− requestURL := strings.TrimSuffix(providerConfig.OIDCConfig.IssuerURL, "/") + "/.well-known/openid-configuration"
214− body, err := requests.New(requestURL).
215− Do().
216− UnmarshalJSON()
217− if err != nil {
218− return nil, fmt.Errorf("failed to discover OIDC configuration: %v", err)
219− }
220−
221− // Prefer manually configured URLs. It's a bit unclear
222− // why you'd be doing discovery and also providing the URLs
223− // explicitly though...
224− if providerConfig.LoginURL == "" {
225− providerConfig.LoginURL = body.Get("authorization_endpoint").MustString()
226− }
227−
228− if providerConfig.RedeemURL == "" {
229− providerConfig.RedeemURL = body.Get("token_endpoint").MustString()
230− }
231−
232− if providerConfig.OIDCConfig.JwksURL == "" {
233− providerConfig.OIDCConfig.JwksURL = body.Get("jwks_uri").MustString()
234− }
235−
236− if providerConfig.ProfileURL == "" {
237− providerConfig.ProfileURL = body.Get("userinfo_endpoint").MustString()
238− }
239−
240− // Now we have performed the discovery, construct the verifier manually
241− return newSkipDiscoveryOIDCVerifier(providerConfig)
242−}
243−
244−func newSkipDiscoveryOIDCVerifier(providerConfig options.Provider) (*internaloidc.IDTokenVerifier, error) {
245− var errs []error
246−
247− // Construct a manual IDTokenVerifier from issuer URL & JWKS URI
248− // instead of metadata discovery if we enable -skip-oidc-discovery.
249− // In this case we need to make sure the required endpoints for
250− // the provider are configured.
251− if providerConfig.LoginURL == "" {
252− errs = append(errs, errors.New("missing required setting: login-url"))
253− }
254− if providerConfig.RedeemURL == "" {
255− errs = append(errs, errors.New("missing required setting: redeem-url"))
256− }
257− if providerConfig.OIDCConfig.JwksURL == "" {
258− errs = append(errs, errors.New("missing required setting: oidc-jwks-url"))
259− }
260− if len(errs) > 0 {
261− return nil, k8serrors.NewAggregate(errs)
262− }
263−
264− keySet := oidc.NewRemoteKeySet(context.TODO(), providerConfig.OIDCConfig.JwksURL)
265− verificationOptions := &internaloidc.IDTokenVerificationOptions{
266− AudienceClaims: providerConfig.OIDCConfig.AudienceClaims,
267− ClientID: providerConfig.ClientID,
268− ExtraAudiences: providerConfig.OIDCConfig.ExtraAudiences,
269− }
270− verifier := internaloidc.NewVerifier(oidc.NewVerifier(providerConfig.OIDCConfig.IssuerURL, keySet, &oidc.Config{
271− ClientID: providerConfig.ClientID,
272− SkipIssuerCheck: providerConfig.OIDCConfig.InsecureSkipIssuerVerification,
273− SkipClientIDCheck: true, // client id check is done within oauth2-proxy: IDTokenVerifier.Verify
274− }), verificationOptions)
275− return verifier, nil
276−}
providers/providers_test.go+1−1
@@ -87,7 +87,7 @@ func TestSkipOIDCDiscovery(t *testing.T) {
8787 }
8888
8989 _, err := newProviderDataFromConfig(providerConfig)
90− g.Expect(err).To(MatchError("error setting OIDC configuration: [missing required setting: login-url, missing required setting: redeem-url, missing required setting: oidc-jwks-url]"))
90+ g.Expect(err).To(MatchError("error building OIDC ProviderVerifier: invalid provider verifier options: missing required setting: jwks-url"))
9191
9292 providerConfig.LoginURL = msAuthURL
9393 providerConfig.RedeemURL = msTokenURL