This commit is contained in:
Brian McNamara 2025-12-09 13:04:40 +01:00 committed by GitHub
commit a44acfdcc7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 85 additions and 8 deletions

View File

@ -9,6 +9,7 @@
## Changes since v7.13.0 ## Changes since v7.13.0
- [#3197](https://github.com/oauth2-proxy/oauth2-proxy/pull/3197) fix: NewRemoteKeySet is not using DefaultHTTPClient (@rsrdesarrollo / @tuunit) - [#3197](https://github.com/oauth2-proxy/oauth2-proxy/pull/3197) fix: NewRemoteKeySet is not using DefaultHTTPClient (@rsrdesarrollo / @tuunit)
- [#2869](https://github.com/oauth2-proxy/oauth2-proxy/pull/2869) feat: pass along oidc config to verifier (@brian-mcnamara)
# V7.13.0 # V7.13.0

View File

@ -17,6 +17,7 @@ import (
// ProviderVerifier represents the OIDC discovery and verification process // ProviderVerifier represents the OIDC discovery and verification process
type ProviderVerifier interface { type ProviderVerifier interface {
DiscoveryEnabled() bool DiscoveryEnabled() bool
JwksURL() string
Provider() DiscoveryProvider Provider() DiscoveryProvider
Verifier() IDTokenVerifier Verifier() IDTokenVerifier
} }
@ -120,6 +121,7 @@ func NewProviderVerifier(ctx context.Context, opts ProviderVerifierOptions) (Pro
return &providerVerifier{ return &providerVerifier{
discoveryEnabled: !opts.SkipDiscovery, discoveryEnabled: !opts.SkipDiscovery,
jwksURL: opts.JWKsURL,
provider: provider, provider: provider,
verifier: verifier, verifier: verifier,
}, nil }, nil
@ -215,6 +217,7 @@ func newVerifierBuilder(issuerURL string, keySet oidc.KeySet, supportedSigningAl
// providerVerifier is an implementation of the ProviderVerifier interface // providerVerifier is an implementation of the ProviderVerifier interface
type providerVerifier struct { type providerVerifier struct {
discoveryEnabled bool discoveryEnabled bool
jwksURL string
provider DiscoveryProvider provider DiscoveryProvider
verifier IDTokenVerifier verifier IDTokenVerifier
} }
@ -234,3 +237,8 @@ func (p *providerVerifier) Provider() DiscoveryProvider {
func (p *providerVerifier) Verifier() IDTokenVerifier { func (p *providerVerifier) Verifier() IDTokenVerifier {
return p.verifier return p.verifier
} }
// JwksURL retuns the jwks url used for the provider
func (p *providerVerifier) JwksURL() string {
return p.jwksURL
}

View File

@ -2,6 +2,8 @@ package oidc
import ( import (
"context" "context"
"net"
"net/http"
"os" "os"
"path/filepath" "path/filepath"
"time" "time"
@ -45,10 +47,24 @@ var _ = AfterSuite(func() {
var _ = Describe("ProviderVerifier", func() { var _ = Describe("ProviderVerifier", func() {
var m *mockoidc.MockOIDC var m *mockoidc.MockOIDC
var endpointsHit []string
BeforeEach(func() { BeforeEach(func() {
var err error var err error
m, err = mockoidc.Run() m, err = mockoidc.NewServer(nil)
Expect(err).ToNot(HaveOccurred())
ln, err := net.Listen("tcp", "127.0.0.1:0")
Expect(err).ToNot(HaveOccurred())
endpointsHit = []string{}
m.AddMiddleware(func(next http.Handler) http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
endpointsHit = append(endpointsHit, req.URL.Path)
next.ServeHTTP(rw, req)
})
})
err = m.Start(ln, nil)
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
}) })
@ -88,9 +104,12 @@ var _ = Describe("ProviderVerifier", func() {
Expect(endpoints.TokenURL).To(Equal(m.TokenEndpoint())) Expect(endpoints.TokenURL).To(Equal(m.TokenEndpoint()))
Expect(endpoints.JWKsURL).To(Equal(m.JWKSEndpoint())) Expect(endpoints.JWKsURL).To(Equal(m.JWKSEndpoint()))
Expect(endpoints.UserInfoURL).To(Equal(m.UserinfoEndpoint())) Expect(endpoints.UserInfoURL).To(Equal(m.UserinfoEndpoint()))
Expect(endpointsHit).To(ContainElement("/oidc/.well-known/openid-configuration"))
} else {
Expect(endpointsHit).ToNot(ContainElement("/oidc/.well-known/openid-configuration"))
} }
}, },
Entry("should be succesfful when discovering the OIDC provider", &newProviderVerifierTableInput{ Entry("should be successful when discovering the OIDC provider", &newProviderVerifierTableInput{
modifyOpts: func(_ *ProviderVerifierOptions) {}, modifyOpts: func(_ *ProviderVerifierOptions) {},
}), }),
Entry("when the issuer URL is missing", &newProviderVerifierTableInput{ Entry("when the issuer URL is missing", &newProviderVerifierTableInput{

View File

@ -59,8 +59,7 @@ func Validate(o *options.Options) error {
jwtIssuers, msgs = parseJwtIssuers(o.ExtraJwtIssuers, msgs) jwtIssuers, msgs = parseJwtIssuers(o.ExtraJwtIssuers, msgs)
for _, jwtIssuer := range jwtIssuers { for _, jwtIssuer := range jwtIssuers {
verifier, err := newVerifierFromJwtIssuer( verifier, err := newVerifierFromJwtIssuer(
o.Providers[0].OIDCConfig.AudienceClaims, o.Providers[0].OIDCConfig,
o.Providers[0].OIDCConfig.ExtraAudiences,
jwtIssuer, jwtIssuer,
) )
if err != nil { if err != nil {
@ -143,12 +142,26 @@ func parseJwtIssuers(issuers []string, msgs []string) ([]jwtIssuer, []string) {
// newVerifierFromJwtIssuer takes in issuer information in jwtIssuer info and returns // newVerifierFromJwtIssuer takes in issuer information in jwtIssuer info and returns
// a verifier for that issuer. // a verifier for that issuer.
func newVerifierFromJwtIssuer(audienceClaims []string, extraAudiences []string, jwtIssuer jwtIssuer) (internaloidc.IDTokenVerifier, error) { func newVerifierFromJwtIssuer(odicOptions options.OIDCOptions, jwtIssuer jwtIssuer) (internaloidc.IDTokenVerifier, error) {
pv, err := newProviderVerifierFromJwtIssuer(odicOptions, jwtIssuer)
if err != nil {
return nil, err
}
return pv.Verifier(), nil
}
// newProviderVerifierFromJwtIssuer takes in issuer information in jwtIssuer info and returns
// a ProviderVerifier for that issuer.
func newProviderVerifierFromJwtIssuer(odicOptions options.OIDCOptions, jwtIssuer jwtIssuer) (internaloidc.ProviderVerifier, error) {
pvOpts := internaloidc.ProviderVerifierOptions{ pvOpts := internaloidc.ProviderVerifierOptions{
AudienceClaims: audienceClaims, AudienceClaims: odicOptions.AudienceClaims,
ClientID: jwtIssuer.audience, ClientID: jwtIssuer.audience,
ExtraAudiences: extraAudiences, ExtraAudiences: odicOptions.ExtraAudiences,
IssuerURL: jwtIssuer.issuerURI, IssuerURL: jwtIssuer.issuerURI,
SkipDiscovery: odicOptions.SkipDiscovery,
JWKsURL: odicOptions.JwksURL,
} }
pv, err := internaloidc.NewProviderVerifier(context.TODO(), pvOpts) pv, err := internaloidc.NewProviderVerifier(context.TODO(), pvOpts)
@ -163,7 +176,7 @@ func newVerifierFromJwtIssuer(audienceClaims []string, extraAudiences []string,
} }
} }
return pv.Verifier(), nil return pv, nil
} }
// jwtIssuer hold parsed JWT issuer info that's used to construct a verifier. // jwtIssuer hold parsed JWT issuer info that's used to construct a verifier.

View File

@ -236,3 +236,39 @@ func TestProviderCAFilesError(t *testing.T) {
assert.Error(t, err) assert.Error(t, err)
assert.Contains(t, err.Error(), "unable to load provider CA file(s)") assert.Contains(t, err.Error(), "unable to load provider CA file(s)")
} }
func TestProviderVerifierSkipsDiscoveryWhenConfigured(t *testing.T) {
jwksUrl := "https://examle.com/auth/certs"
o := testOptions()
o.Providers[0].OIDCConfig.SkipDiscovery = true
o.Providers[0].OIDCConfig.JwksURL = jwksUrl
jwtIssuer := jwtIssuer{
issuerURI: "https://example.com",
audience: "aud",
}
pv, err := newProviderVerifierFromJwtIssuer(o.Providers[0].OIDCConfig, jwtIssuer)
assert.Equal(t, nil, err)
assert.NotEqual(t, nil, pv)
assert.Equal(t, jwksUrl, pv.JwksURL())
assert.False(t, pv.DiscoveryEnabled())
}
func TestProviderVerifierUsesFallback(t *testing.T) {
issuerURI := "https://example.com"
o := testOptions()
jwtIssuer := jwtIssuer{
issuerURI: issuerURI,
audience: "aud",
}
pv, err := newProviderVerifierFromJwtIssuer(o.Providers[0].OIDCConfig, jwtIssuer)
assert.Equal(t, nil, err)
assert.NotEqual(t, nil, pv)
assert.Equal(t, issuerURI+"/.well-known/jwks.json", pv.JwksURL())
assert.False(t, pv.DiscoveryEnabled())
}