diff --git a/pkg/providers/oidc/verifier.go b/pkg/providers/oidc/verifier.go index c0ad9b8..34c4a00 100755 --- a/pkg/providers/oidc/verifier.go +++ b/pkg/providers/oidc/verifier.go @@ -8,33 +8,42 @@ import ( "github.com/coreos/go-oidc/v3/oidc" ) -// IDTokenVerifier Used to verify an ID Token and extends oidc.IDTokenVerifier from the underlying oidc library -type IDTokenVerifier struct { - *oidc.IDTokenVerifier - *IDTokenVerificationOptions - allowedAudiences map[string]struct{} +// idTokenVerifier allows an ID Token to be verified against the issue and provided keys. +type IDTokenVerifier interface { + Verify(context.Context, string) (*oidc.IDToken, error) } -// IDTokenVerificationOptions options for the oidc.IDTokenVerifier that are required to verify an ID Token +// idTokenVerifier Used to verify an ID Token and extends oidc.idTokenVerifier from the underlying oidc library +type idTokenVerifier struct { + verifier *oidc.IDTokenVerifier + verificationOptions IDTokenVerificationOptions + allowedAudiences map[string]struct{} +} + +// IDTokenVerificationOptions options for the oidc.idTokenVerifier that are required to verify an ID Token type IDTokenVerificationOptions struct { AudienceClaims []string ClientID string ExtraAudiences []string } -// NewVerifier constructs a new IDTokenVerifier -func NewVerifier(iv *oidc.IDTokenVerifier, vo *IDTokenVerificationOptions) *IDTokenVerifier { +// NewVerifier constructs a new idTokenVerifier +func NewVerifier(iv *oidc.IDTokenVerifier, vo IDTokenVerificationOptions) IDTokenVerifier { allowedAudiences := make(map[string]struct{}) allowedAudiences[vo.ClientID] = struct{}{} for _, extraAudience := range vo.ExtraAudiences { allowedAudiences[extraAudience] = struct{}{} } - return &IDTokenVerifier{iv, vo, allowedAudiences} + return &idTokenVerifier{ + verifier: iv, + verificationOptions: vo, + allowedAudiences: allowedAudiences, + } } // Verify verifies incoming ID Token -func (v *IDTokenVerifier) Verify(ctx context.Context, rawIDToken string) (*oidc.IDToken, error) { - token, err := v.IDTokenVerifier.Verify(ctx, rawIDToken) +func (v *idTokenVerifier) Verify(ctx context.Context, rawIDToken string) (*oidc.IDToken, error) { + token, err := v.verifier.Verify(ctx, rawIDToken) if err != nil { return nil, fmt.Errorf("failed to verify token: %v", err) } @@ -51,8 +60,8 @@ func (v *IDTokenVerifier) Verify(ctx context.Context, rawIDToken string) (*oidc. return token, err } -func (v *IDTokenVerifier) verifyAudience(token *oidc.IDToken, claims map[string]interface{}) (bool, error) { - for _, audienceClaim := range v.AudienceClaims { +func (v *idTokenVerifier) verifyAudience(token *oidc.IDToken, claims map[string]interface{}) (bool, error) { + for _, audienceClaim := range v.verificationOptions.AudienceClaims { if audienceClaimValue, audienceClaimExists := claims[audienceClaim]; audienceClaimExists { // audience claim value can be either interface{} or []interface{}, @@ -72,10 +81,10 @@ func (v *IDTokenVerifier) verifyAudience(token *oidc.IDToken, claims map[string] } return false, fmt.Errorf("audience claims %v do not exist in claims: %v", - v.AudienceClaims, claims) + v.verificationOptions.AudienceClaims, claims) } -func (v *IDTokenVerifier) isValidAudience(claim string, audience []string, allowedAudiences map[string]struct{}) (bool, error) { +func (v *idTokenVerifier) isValidAudience(claim string, audience []string, allowedAudiences map[string]struct{}) (bool, error) { for _, aud := range audience { if _, allowedAudienceExists := allowedAudiences[aud]; allowedAudienceExists { return true, nil @@ -87,7 +96,7 @@ func (v *IDTokenVerifier) isValidAudience(claim string, audience []string, allow claim, audience, allowedAudiences) } -func (v *IDTokenVerifier) interfaceSliceToString(slice interface{}) []string { +func (v *idTokenVerifier) interfaceSliceToString(slice interface{}) []string { s := reflect.ValueOf(slice) if s.Kind() != reflect.Slice { panic(fmt.Sprintf("given a non-slice type %s", s.Kind())) diff --git a/pkg/providers/oidc/verifier_test.go b/pkg/providers/oidc/verifier_test.go index a9aaece..c82de7a 100755 --- a/pkg/providers/oidc/verifier_test.go +++ b/pkg/providers/oidc/verifier_test.go @@ -17,7 +17,7 @@ var _ = Describe("Verify", func() { ctx := context.Background() It("Succeeds with default aud behavior", func() { - result, err := verify(ctx, &IDTokenVerificationOptions{ + result, err := verify(ctx, IDTokenVerificationOptions{ AudienceClaims: []string{"aud"}, ClientID: "1226737", ExtraAudiences: []string{}, @@ -32,7 +32,7 @@ var _ = Describe("Verify", func() { }) It("Fails with default aud behavior", func() { - result, err := verify(ctx, &IDTokenVerificationOptions{ + result, err := verify(ctx, IDTokenVerificationOptions{ AudienceClaims: []string{"aud"}, ClientID: "7817818", ExtraAudiences: []string{}, @@ -46,7 +46,7 @@ var _ = Describe("Verify", func() { }) It("Succeeds with extra audiences", func() { - result, err := verify(ctx, &IDTokenVerificationOptions{ + result, err := verify(ctx, IDTokenVerificationOptions{ AudienceClaims: []string{"aud"}, ClientID: "7817818", ExtraAudiences: []string{"xyz", "1226737"}, @@ -61,7 +61,7 @@ var _ = Describe("Verify", func() { }) It("Fails with extra audiences", func() { - result, err := verify(ctx, &IDTokenVerificationOptions{ + result, err := verify(ctx, IDTokenVerificationOptions{ AudienceClaims: []string{"aud"}, ClientID: "7817818", ExtraAudiences: []string{"xyz", "abc"}, @@ -76,7 +76,7 @@ var _ = Describe("Verify", func() { }) It("Succeeds with non default aud behavior", func() { - result, err := verify(ctx, &IDTokenVerificationOptions{ + result, err := verify(ctx, IDTokenVerificationOptions{ AudienceClaims: []string{"client_id"}, ClientID: "1226737", ExtraAudiences: []string{}, @@ -91,7 +91,7 @@ var _ = Describe("Verify", func() { }) It("Fails with non default aud behavior", func() { - result, err := verify(ctx, &IDTokenVerificationOptions{ + result, err := verify(ctx, IDTokenVerificationOptions{ AudienceClaims: []string{"client_id"}, ClientID: "7817818", ExtraAudiences: []string{}, @@ -105,7 +105,7 @@ var _ = Describe("Verify", func() { }) It("Succeeds with non default aud behavior and extra audiences", func() { - result, err := verify(ctx, &IDTokenVerificationOptions{ + result, err := verify(ctx, IDTokenVerificationOptions{ AudienceClaims: []string{"client_id"}, ClientID: "7817818", ExtraAudiences: []string{"xyz", "1226737"}, @@ -120,7 +120,7 @@ var _ = Describe("Verify", func() { }) It("Fails with non default aud behavior and extra audiences", func() { - result, err := verify(ctx, &IDTokenVerificationOptions{ + result, err := verify(ctx, IDTokenVerificationOptions{ AudienceClaims: []string{"client_id"}, ClientID: "7817818", ExtraAudiences: []string{"xyz", "abc"}, @@ -135,7 +135,7 @@ var _ = Describe("Verify", func() { }) It("Fails if audience claim does not exist", func() { - result, err := verify(ctx, &IDTokenVerificationOptions{ + result, err := verify(ctx, IDTokenVerificationOptions{ AudienceClaims: []string{"not_exists"}, ClientID: "7817818", ExtraAudiences: []string{"xyz", "abc"}, @@ -151,7 +151,7 @@ var _ = Describe("Verify", func() { }) It("Succeeds with multiple audiences", func() { - var result, err = verify(ctx, &IDTokenVerificationOptions{ + var result, err = verify(ctx, IDTokenVerificationOptions{ AudienceClaims: []string{"client_id", "aud"}, ClientID: "123456789", ExtraAudiences: []string{"1226737"}, @@ -167,7 +167,7 @@ var _ = Describe("Verify", func() { }) It("Succeeds if aud claim match", func() { - result, err := verify(ctx, &IDTokenVerificationOptions{ + result, err := verify(ctx, IDTokenVerificationOptions{ AudienceClaims: []string{"client_id", "aud"}, ClientID: "1226737", ExtraAudiences: []string{"xyz", "abc"}, @@ -207,7 +207,7 @@ func (t *testVerifier) VerifySignature(ctx context.Context, jwt string) ([]byte, return jws.Verify(&t.jwk) } -func verify(ctx context.Context, verificationOptions *IDTokenVerificationOptions, payload payload) (*oidc.IDToken, error) { +func verify(ctx context.Context, verificationOptions IDTokenVerificationOptions, payload payload) (*oidc.IDToken, error) { config := &oidc.Config{ ClientID: "1226737", SkipClientIDCheck: true, diff --git a/providers/adfs_test.go b/providers/adfs_test.go index 0822b99..19356b7 100755 --- a/providers/adfs_test.go +++ b/providers/adfs_test.go @@ -43,7 +43,7 @@ func newSignedTestADFSToken(tokenClaims adfsClaims) (string, error) { } func testADFSProvider(hostname string) *ADFSProvider { - verificationOptions := &internaloidc.IDTokenVerificationOptions{ + verificationOptions := internaloidc.IDTokenVerificationOptions{ AudienceClaims: []string{"aud"}, ClientID: "https://test.myapp.com", } diff --git a/providers/azure_test.go b/providers/azure_test.go index 1341903..794ed57 100644 --- a/providers/azure_test.go +++ b/providers/azure_test.go @@ -41,7 +41,7 @@ type azureOAuthPayload struct { } func testAzureProvider(hostname string, opts options.AzureOptions) *AzureProvider { - verificationOptions := &internaloidc.IDTokenVerificationOptions{ + verificationOptions := internaloidc.IDTokenVerificationOptions{ AudienceClaims: []string{"aud"}, ClientID: "cd6d4fae-f6a6-4a34-8454-2c6b598e9532", } diff --git a/providers/keycloak_oidc_test.go b/providers/keycloak_oidc_test.go index 4488c46..d1a50b2 100644 --- a/providers/keycloak_oidc_test.go +++ b/providers/keycloak_oidc_test.go @@ -45,7 +45,7 @@ func newTestKeycloakOIDCSetup() (*httptest.Server, *KeycloakOIDCProvider) { } func newKeycloakOIDCProvider(serverURL *url.URL, opts options.KeycloakOptions) *KeycloakOIDCProvider { - verificationOptions := &internaloidc.IDTokenVerificationOptions{ + verificationOptions := internaloidc.IDTokenVerificationOptions{ AudienceClaims: []string{defaultAudienceClaim}, ClientID: mockClientID, } diff --git a/providers/oidc_test.go b/providers/oidc_test.go index 2fe6064..1341830 100644 --- a/providers/oidc_test.go +++ b/providers/oidc_test.go @@ -27,7 +27,7 @@ type redeemTokenResponse struct { } func newOIDCProvider(serverURL *url.URL, skipNonce bool) *OIDCProvider { - verificationOptions := &internaloidc.IDTokenVerificationOptions{ + verificationOptions := internaloidc.IDTokenVerificationOptions{ AudienceClaims: []string{"aud"}, ClientID: "https://test.myapp.com", } diff --git a/providers/provider_data.go b/providers/provider_data.go index 096d470..9bcb872 100644 --- a/providers/provider_data.go +++ b/providers/provider_data.go @@ -47,7 +47,7 @@ type ProviderData struct { UserClaim string EmailClaim string GroupsClaim string - Verifier *internaloidc.IDTokenVerifier + Verifier internaloidc.IDTokenVerifier // Universal Group authorization data structure // any provider can set to consume diff --git a/providers/provider_data_test.go b/providers/provider_data_test.go index ecdd18c..40892cb 100644 --- a/providers/provider_data_test.go +++ b/providers/provider_data_test.go @@ -202,7 +202,7 @@ func TestProviderData_verifyIDToken(t *testing.T) { provider := &ProviderData{} if tc.Verifier { - verificationOptions := &internaloidc.IDTokenVerificationOptions{ + verificationOptions := internaloidc.IDTokenVerificationOptions{ AudienceClaims: []string{"aud"}, ClientID: oidcClientID, } @@ -409,7 +409,7 @@ func TestProviderData_buildSessionFromClaims(t *testing.T) { t.Run(testName, func(t *testing.T) { g := NewWithT(t) - verificationOptions := &internaloidc.IDTokenVerificationOptions{ + verificationOptions := internaloidc.IDTokenVerificationOptions{ AudienceClaims: []string{"aud"}, ClientID: oidcClientID, } @@ -478,7 +478,7 @@ func TestProviderData_checkNonce(t *testing.T) { g.Expect(err).ToNot(HaveOccurred()) tc.Session.IDToken = rawIDToken - verificationOptions := &internaloidc.IDTokenVerificationOptions{ + verificationOptions := internaloidc.IDTokenVerificationOptions{ AudienceClaims: []string{"aud"}, ClientID: oidcClientID, } diff --git a/providers/providers.go b/providers/providers.go index 0c30600..d120b2f 100644 --- a/providers/providers.go +++ b/providers/providers.go @@ -160,7 +160,7 @@ func providerRequiresOIDCProviderVerifier(providerType options.ProviderType) (bo } } -func newOIDCProviderVerifier(providerConfig options.Provider) (*oidc.Provider, *internaloidc.IDTokenVerifier, error) { +func newOIDCProviderVerifier(providerConfig options.Provider) (*oidc.Provider, internaloidc.IDTokenVerifier, error) { // If the issuer isn't set, default it for platforms where it makes sense if providerConfig.OIDCConfig.IssuerURL == "" { switch providerConfig.Type { @@ -183,13 +183,13 @@ func newOIDCProviderVerifier(providerConfig options.Provider) (*oidc.Provider, * } } -func newDiscoveryOIDCProviderVerifier(providerConfig options.Provider) (*oidc.Provider, *internaloidc.IDTokenVerifier, error) { +func newDiscoveryOIDCProviderVerifier(providerConfig options.Provider) (*oidc.Provider, internaloidc.IDTokenVerifier, error) { // Configure discoverable provider data. provider, err := oidc.NewProvider(context.TODO(), providerConfig.OIDCConfig.IssuerURL) if err != nil { return nil, nil, err } - verificationOptions := &internaloidc.IDTokenVerificationOptions{ + verificationOptions := internaloidc.IDTokenVerificationOptions{ AudienceClaims: providerConfig.OIDCConfig.AudienceClaims, ClientID: providerConfig.ClientID, ExtraAudiences: providerConfig.OIDCConfig.ExtraAudiences, @@ -203,7 +203,7 @@ func newDiscoveryOIDCProviderVerifier(providerConfig options.Provider) (*oidc.Pr return provider, verifier, nil } -func newInsecureSkipIssuerVerificationOIDCVerifier(providerConfig options.Provider) (*internaloidc.IDTokenVerifier, error) { +func newInsecureSkipIssuerVerificationOIDCVerifier(providerConfig options.Provider) (internaloidc.IDTokenVerifier, error) { // go-oidc doesn't let us pass bypass the issuer check this in the oidc.NewProvider call // (which uses discovery to get the URLs), so we'll do a quick check ourselves and if // we get the URLs, we'll just use the non-discovery path. @@ -241,7 +241,7 @@ func newInsecureSkipIssuerVerificationOIDCVerifier(providerConfig options.Provid return newSkipDiscoveryOIDCVerifier(providerConfig) } -func newSkipDiscoveryOIDCVerifier(providerConfig options.Provider) (*internaloidc.IDTokenVerifier, error) { +func newSkipDiscoveryOIDCVerifier(providerConfig options.Provider) (internaloidc.IDTokenVerifier, error) { var errs []error // Construct a manual IDTokenVerifier from issuer URL & JWKS URI @@ -262,7 +262,7 @@ func newSkipDiscoveryOIDCVerifier(providerConfig options.Provider) (*internaloid } keySet := oidc.NewRemoteKeySet(context.TODO(), providerConfig.OIDCConfig.JwksURL) - verificationOptions := &internaloidc.IDTokenVerificationOptions{ + verificationOptions := internaloidc.IDTokenVerificationOptions{ AudienceClaims: providerConfig.OIDCConfig.AudienceClaims, ClientID: providerConfig.ClientID, ExtraAudiences: providerConfig.OIDCConfig.ExtraAudiences,