174 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			174 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			Go
		
	
	
	
| package oidc
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/dgrijalva/jwt-go"
 | |
| 	"github.com/oauth2-proxy/mockoidc"
 | |
| 	. "github.com/onsi/ginkgo"
 | |
| 	. "github.com/onsi/ginkgo/extensions/table"
 | |
| 	. "github.com/onsi/gomega"
 | |
| )
 | |
| 
 | |
| var _ = Describe("ProviderVerifier", func() {
 | |
| 	var m *mockoidc.MockOIDC
 | |
| 
 | |
| 	BeforeEach(func() {
 | |
| 		var err error
 | |
| 		m, err = mockoidc.Run()
 | |
| 		Expect(err).ToNot(HaveOccurred())
 | |
| 	})
 | |
| 
 | |
| 	AfterEach(func() {
 | |
| 		Expect(m.Shutdown()).To(Succeed())
 | |
| 	})
 | |
| 
 | |
| 	type newProviderVerifierTableInput struct {
 | |
| 		modifyOpts    func(*ProviderVerifierOptions)
 | |
| 		expectedError string
 | |
| 	}
 | |
| 
 | |
| 	DescribeTable("when constructing the provider verifier", func(in *newProviderVerifierTableInput) {
 | |
| 		opts := ProviderVerifierOptions{
 | |
| 			AudienceClaims: []string{"aud"},
 | |
| 			ClientID:       m.Config().ClientID,
 | |
| 			ExtraAudiences: []string{},
 | |
| 			IssuerURL:      m.Issuer(),
 | |
| 		}
 | |
| 		if in.modifyOpts != nil {
 | |
| 			in.modifyOpts(&opts)
 | |
| 		}
 | |
| 
 | |
| 		pv, err := NewProviderVerifier(context.Background(), opts)
 | |
| 		if in.expectedError != "" {
 | |
| 			Expect(err).To(MatchError(HavePrefix(in.expectedError)))
 | |
| 			return
 | |
| 		}
 | |
| 		Expect(err).ToNot(HaveOccurred())
 | |
| 
 | |
| 		Expect(pv.DiscoveryEnabled()).ToNot(Equal(opts.SkipDiscovery), "DiscoveryEnabled should be the reverse of skip discovery")
 | |
| 		Expect(pv.Provider()).ToNot(BeNil())
 | |
| 
 | |
| 		if pv.DiscoveryEnabled() {
 | |
| 			endpoints := pv.Provider().Endpoints()
 | |
| 			Expect(endpoints.AuthURL).To(Equal(m.AuthorizationEndpoint()))
 | |
| 			Expect(endpoints.TokenURL).To(Equal(m.TokenEndpoint()))
 | |
| 			Expect(endpoints.JWKsURL).To(Equal(m.JWKSEndpoint()))
 | |
| 			Expect(endpoints.UserInfoURL).To(Equal(m.UserinfoEndpoint()))
 | |
| 		}
 | |
| 	},
 | |
| 		Entry("should be succesfful when discovering the OIDC provider", &newProviderVerifierTableInput{
 | |
| 			modifyOpts: func(_ *ProviderVerifierOptions) {},
 | |
| 		}),
 | |
| 		Entry("when the issuer URL is missing", &newProviderVerifierTableInput{
 | |
| 			modifyOpts: func(p *ProviderVerifierOptions) {
 | |
| 				p.IssuerURL = ""
 | |
| 			},
 | |
| 			expectedError: "invalid provider verifier options: missing required setting: issuer-url",
 | |
| 		}),
 | |
| 		Entry("when the issuer URL is invalid", &newProviderVerifierTableInput{
 | |
| 			modifyOpts: func(p *ProviderVerifierOptions) {
 | |
| 				p.IssuerURL = "invalid"
 | |
| 			},
 | |
| 			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 \"\"",
 | |
| 		}),
 | |
| 		Entry("with skip discovery and the JWKs URL is missing", &newProviderVerifierTableInput{
 | |
| 			modifyOpts: func(p *ProviderVerifierOptions) {
 | |
| 				p.SkipDiscovery = true
 | |
| 				p.JWKsURL = ""
 | |
| 			},
 | |
| 			expectedError: "invalid provider verifier options: missing required setting: jwks-url",
 | |
| 		}),
 | |
| 		Entry("should be succesfful when skipping discovery with the JWKs URL specified", &newProviderVerifierTableInput{
 | |
| 			modifyOpts: func(p *ProviderVerifierOptions) {
 | |
| 				p.SkipDiscovery = true
 | |
| 				p.JWKsURL = m.JWKSEndpoint()
 | |
| 			},
 | |
| 		}),
 | |
| 	)
 | |
| 
 | |
| 	type verifierTableInput struct {
 | |
| 		modifyOpts    func(*ProviderVerifierOptions)
 | |
| 		modifyClaims  func(*jwt.StandardClaims)
 | |
| 		expectedError string
 | |
| 	}
 | |
| 
 | |
| 	DescribeTable("when constructing the provider verifier", func(in *verifierTableInput) {
 | |
| 		opts := ProviderVerifierOptions{
 | |
| 			AudienceClaims: []string{"aud"},
 | |
| 			ClientID:       m.Config().ClientID,
 | |
| 			ExtraAudiences: []string{},
 | |
| 			IssuerURL:      m.Issuer(),
 | |
| 		}
 | |
| 		if in.modifyOpts != nil {
 | |
| 			in.modifyOpts(&opts)
 | |
| 		}
 | |
| 
 | |
| 		pv, err := NewProviderVerifier(context.Background(), opts)
 | |
| 		Expect(err).ToNot(HaveOccurred())
 | |
| 
 | |
| 		now := time.Now()
 | |
| 		claims := jwt.StandardClaims{
 | |
| 			Audience:  m.Config().ClientID,
 | |
| 			Issuer:    m.Issuer(),
 | |
| 			ExpiresAt: now.Add(1 * time.Hour).Unix(),
 | |
| 			IssuedAt:  now.Unix(),
 | |
| 			Subject:   "user",
 | |
| 		}
 | |
| 		if in.modifyClaims != nil {
 | |
| 			in.modifyClaims(&claims)
 | |
| 		}
 | |
| 
 | |
| 		rawIDToken, err := m.Keypair.SignJWT(claims)
 | |
| 		Expect(err).ToNot(HaveOccurred())
 | |
| 
 | |
| 		idToken, err := pv.Verifier().Verify(context.Background(), rawIDToken)
 | |
| 		if in.expectedError != "" {
 | |
| 			Expect(err).To(MatchError(HavePrefix(in.expectedError)))
 | |
| 			return
 | |
| 		}
 | |
| 		Expect(err).ToNot(HaveOccurred())
 | |
| 
 | |
| 		Expect(idToken.Issuer).To(Equal(claims.Issuer))
 | |
| 		Expect(idToken.Audience).To(ConsistOf(claims.Audience))
 | |
| 		Expect(idToken.Subject).To(Equal(claims.Subject))
 | |
| 	},
 | |
| 		Entry("with the default opts and claims", &verifierTableInput{}),
 | |
| 		Entry("when the audience is mismatched", &verifierTableInput{
 | |
| 			modifyClaims: func(j *jwt.StandardClaims) {
 | |
| 				j.Audience = "OtherClient"
 | |
| 			},
 | |
| 			expectedError: "audience from claim aud with value [OtherClient] does not match with any of allowed audiences",
 | |
| 		}),
 | |
| 		Entry("when the audience is an extra audience", &verifierTableInput{
 | |
| 			modifyOpts: func(p *ProviderVerifierOptions) {
 | |
| 				p.ExtraAudiences = []string{"ExtraIssuer"}
 | |
| 			},
 | |
| 			modifyClaims: func(j *jwt.StandardClaims) {
 | |
| 				j.Audience = "ExtraIssuer"
 | |
| 			},
 | |
| 		}),
 | |
| 		Entry("when the issuer is mismatched", &verifierTableInput{
 | |
| 			modifyClaims: func(j *jwt.StandardClaims) {
 | |
| 				j.Issuer = "OtherIssuer"
 | |
| 			},
 | |
| 			expectedError: "failed to verify token: oidc: id token issued by a different provider",
 | |
| 		}),
 | |
| 		Entry("when the issuer is mismatched with skip issuer verification", &verifierTableInput{
 | |
| 			modifyOpts: func(p *ProviderVerifierOptions) {
 | |
| 				p.SkipIssuerVerification = true
 | |
| 			},
 | |
| 			modifyClaims: func(j *jwt.StandardClaims) {
 | |
| 				j.Issuer = "OtherIssuer"
 | |
| 			},
 | |
| 		}),
 | |
| 		Entry("when the token has expired", &verifierTableInput{
 | |
| 			modifyClaims: func(j *jwt.StandardClaims) {
 | |
| 				j.ExpiresAt = time.Now().Add(-1 * time.Hour).Unix()
 | |
| 			},
 | |
| 			expectedError: "failed to verify token: oidc: token is expired",
 | |
| 		}),
 | |
| 	)
 | |
| })
 |