git.iko.soy/typeable/oauth2-proxy
on master help sign in
Move OIDC IDToken verifier behind interface
commited3892296e1e3c424d90e125c99f70ac4854daed authorJoel Speed <joel.speed@hotmail.co.uk> dateWed 16 Feb 2022, 14:06 (5 years ago) parent979c3e8
9 files changed+51−42
pkg/providers/oidc/verifier.go+25−16 pkg/providers/oidc/verifier_test.go+12−12 providers/adfs_test.go+1−1 providers/azure_test.go+1−1 providers/keycloak_oidc_test.go+1−1 providers/oidc_test.go+1−1 providers/provider_data.go+1−1 providers/provider_data_test.go+3−3 providers/providers.go+6−6
pkg/providers/oidc/verifier.go+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/providers/oidc/verifier_test.go+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,
providers/adfs_test.go+1−1
@@ -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+1−1
@@ -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+1−1
@@ -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+1−1
@@ -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+1−1
@@ -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+3−3
@@ -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+6−6
@@ -160,7 +160,7 @@ func providerRequiresOIDCProviderVerifier(providerType options.ProviderType) (bo
160160 }
161161}
162162
163−func newOIDCProviderVerifier(providerConfig options.Provider) (*oidc.Provider, *internaloidc.IDTokenVerifier, error) {
163+func newOIDCProviderVerifier(providerConfig options.Provider) (*oidc.Provider, internaloidc.IDTokenVerifier, error) {
164164 // If the issuer isn't set, default it for platforms where it makes sense
165165 if providerConfig.OIDCConfig.IssuerURL == "" {
166166 switch providerConfig.Type {
@@ -183,13 +183,13 @@ func newOIDCProviderVerifier(providerConfig options.Provider) (*oidc.Provider, *
183183 }
184184}
185185
186−func newDiscoveryOIDCProviderVerifier(providerConfig options.Provider) (*oidc.Provider, *internaloidc.IDTokenVerifier, error) {
186+func newDiscoveryOIDCProviderVerifier(providerConfig options.Provider) (*oidc.Provider, internaloidc.IDTokenVerifier, error) {
187187 // Configure discoverable provider data.
188188 provider, err := oidc.NewProvider(context.TODO(), providerConfig.OIDCConfig.IssuerURL)
189189 if err != nil {
190190 return nil, nil, err
191191 }
192− verificationOptions := &internaloidc.IDTokenVerificationOptions{
192+ verificationOptions := internaloidc.IDTokenVerificationOptions{
193193 AudienceClaims: providerConfig.OIDCConfig.AudienceClaims,
194194 ClientID: providerConfig.ClientID,
195195 ExtraAudiences: providerConfig.OIDCConfig.ExtraAudiences,
@@ -203,7 +203,7 @@ func newDiscoveryOIDCProviderVerifier(providerConfig options.Provider) (*oidc.Pr
203203 return provider, verifier, nil
204204}
205205
206−func newInsecureSkipIssuerVerificationOIDCVerifier(providerConfig options.Provider) (*internaloidc.IDTokenVerifier, error) {
206+func newInsecureSkipIssuerVerificationOIDCVerifier(providerConfig options.Provider) (internaloidc.IDTokenVerifier, error) {
207207 // go-oidc doesn't let us pass bypass the issuer check this in the oidc.NewProvider call
208208 // (which uses discovery to get the URLs), so we'll do a quick check ourselves and if
209209 // we get the URLs, we'll just use the non-discovery path.
@@ -241,7 +241,7 @@ func newInsecureSkipIssuerVerificationOIDCVerifier(providerConfig options.Provid
241241 return newSkipDiscoveryOIDCVerifier(providerConfig)
242242}
243243
244−func newSkipDiscoveryOIDCVerifier(providerConfig options.Provider) (*internaloidc.IDTokenVerifier, error) {
244+func newSkipDiscoveryOIDCVerifier(providerConfig options.Provider) (internaloidc.IDTokenVerifier, error) {
245245 var errs []error
246246
247247 // Construct a manual IDTokenVerifier from issuer URL & JWKS URI
@@ -262,7 +262,7 @@ func newSkipDiscoveryOIDCVerifier(providerConfig options.Provider) (*internaloid
262262 }
263263
264264 keySet := oidc.NewRemoteKeySet(context.TODO(), providerConfig.OIDCConfig.JwksURL)
265− verificationOptions := &internaloidc.IDTokenVerificationOptions{
265+ verificationOptions := internaloidc.IDTokenVerificationOptions{
266266 AudienceClaims: providerConfig.OIDCConfig.AudienceClaims,
267267 ClientID: providerConfig.ClientID,
268268 ExtraAudiences: providerConfig.OIDCConfig.ExtraAudiences,