mirror of
https://github.com/oauth2-proxy/oauth2-proxy.git
synced 2024-11-28 09:08:44 +02:00
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",
|
|
}),
|
|
)
|
|
})
|