mirror of
https://github.com/oauth2-proxy/oauth2-proxy.git
synced 2026-10-18 13:18:26 +02:00
Discover signature algorithms from OIDC provider (#1662)
This commit is contained in:
@@ -11,12 +11,13 @@ import (
|
||||
|
||||
// providerJSON resresents the information we need from an OIDC discovery
|
||||
type providerJSON struct {
|
||||
Issuer string `json:"issuer"`
|
||||
AuthURL string `json:"authorization_endpoint"`
|
||||
TokenURL string `json:"token_endpoint"`
|
||||
JWKsURL string `json:"jwks_uri"`
|
||||
UserInfoURL string `json:"userinfo_endpoint"`
|
||||
CodeChallengeAlgs []string `json:"code_challenge_methods_supported"`
|
||||
Issuer string `json:"issuer"`
|
||||
AuthURL string `json:"authorization_endpoint"`
|
||||
TokenURL string `json:"token_endpoint"`
|
||||
JWKsURL string `json:"jwks_uri"`
|
||||
UserInfoURL string `json:"userinfo_endpoint"`
|
||||
CodeChallengeAlgs []string `json:"code_challenge_methods_supported"`
|
||||
SupportedSigningAlgs []string `json:"id_token_signing_alg_values_supported"`
|
||||
}
|
||||
|
||||
// Endpoints represents the endpoints discovered as part of the OIDC discovery process
|
||||
@@ -39,6 +40,7 @@ type PKCE struct {
|
||||
type DiscoveryProvider interface {
|
||||
Endpoints() Endpoints
|
||||
PKCE() PKCE
|
||||
SupportedSigningAlgs() []string
|
||||
}
|
||||
|
||||
// NewProvider allows a user to perform an OIDC discovery and returns the DiscoveryProvider.
|
||||
@@ -63,21 +65,23 @@ func NewProvider(ctx context.Context, issuerURL string, skipIssuerVerification b
|
||||
}
|
||||
|
||||
return &discoveryProvider{
|
||||
authURL: p.AuthURL,
|
||||
tokenURL: p.TokenURL,
|
||||
jwksURL: p.JWKsURL,
|
||||
userInfoURL: p.UserInfoURL,
|
||||
codeChallengeAlgs: p.CodeChallengeAlgs,
|
||||
authURL: p.AuthURL,
|
||||
tokenURL: p.TokenURL,
|
||||
jwksURL: p.JWKsURL,
|
||||
userInfoURL: p.UserInfoURL,
|
||||
codeChallengeAlgs: p.CodeChallengeAlgs,
|
||||
supportedSigningAlgs: p.SupportedSigningAlgs,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// discoveryProvider holds the discovered endpoints
|
||||
type discoveryProvider struct {
|
||||
authURL string
|
||||
tokenURL string
|
||||
jwksURL string
|
||||
userInfoURL string
|
||||
codeChallengeAlgs []string
|
||||
authURL string
|
||||
tokenURL string
|
||||
jwksURL string
|
||||
userInfoURL string
|
||||
codeChallengeAlgs []string
|
||||
supportedSigningAlgs []string
|
||||
}
|
||||
|
||||
// Endpoints returns the discovered endpoints needed for an authentication provider.
|
||||
@@ -96,3 +100,8 @@ func (p *discoveryProvider) PKCE() PKCE {
|
||||
CodeChallengeAlgs: p.codeChallengeAlgs,
|
||||
}
|
||||
}
|
||||
|
||||
// SupportedSigningAlgs returns the discovered provider signing algorithms.
|
||||
func (p *discoveryProvider) SupportedSigningAlgs() []string {
|
||||
return p.supportedSigningAlgs
|
||||
}
|
||||
|
||||
@@ -103,6 +103,25 @@ var _ = Describe("Provider", func() {
|
||||
|
||||
Expect(provider.PKCE().CodeChallengeAlgs).To(ConsistOf("S256", "plain"))
|
||||
})
|
||||
|
||||
It("with signing algorithms supported on the provider, should populate signature information", func() {
|
||||
m, err := mockoidc.NewServer(nil)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
m.AddMiddleware(newSigningAlgsIssuerMiddleware(m))
|
||||
|
||||
ln, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(m.Start(ln, nil)).To(Succeed())
|
||||
defer func() {
|
||||
Expect(m.Shutdown()).To(Succeed())
|
||||
}()
|
||||
|
||||
provider, err := NewProvider(context.Background(), m.Issuer(), false)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(provider.SupportedSigningAlgs()).To(ConsistOf("RS256", "HS256"))
|
||||
})
|
||||
})
|
||||
|
||||
func newInvalidIssuerMiddleware(m *mockoidc.MockOIDC) func(http.Handler) http.Handler {
|
||||
@@ -144,6 +163,26 @@ func newCodeChallengeIssuerMiddleware(m *mockoidc.MockOIDC) func(http.Handler) h
|
||||
}
|
||||
}
|
||||
|
||||
func newSigningAlgsIssuerMiddleware(m *mockoidc.MockOIDC) func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
p := providerJSON{
|
||||
Issuer: m.Issuer(),
|
||||
AuthURL: m.AuthorizationEndpoint(),
|
||||
TokenURL: m.TokenEndpoint(),
|
||||
JWKsURL: m.JWKSEndpoint(),
|
||||
UserInfoURL: m.UserinfoEndpoint(),
|
||||
SupportedSigningAlgs: []string{"RS256", "HS256"},
|
||||
}
|
||||
data, err := json.Marshal(p)
|
||||
if err != nil {
|
||||
rw.WriteHeader(500)
|
||||
}
|
||||
rw.Write(data)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func newBadRequestMiddleware() func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
|
||||
@@ -43,6 +43,10 @@ type ProviderVerifierOptions struct {
|
||||
// SkipIssuerVerification skips verification of ID token issuers.
|
||||
// When false, ID Token Issuers must match the OIDC discovery URL.
|
||||
SkipIssuerVerification bool
|
||||
|
||||
// SupportedSigningAlgs is the list of signature algorithms supported by the
|
||||
// provider.
|
||||
SupportedSigningAlgs []string
|
||||
}
|
||||
|
||||
// validate checks that the required options are present before attempting to create
|
||||
@@ -76,9 +80,10 @@ func (p ProviderVerifierOptions) toVerificationOptions() IDTokenVerificationOpti
|
||||
// toOIDCConfig returns an oidc.Config based on the configured options.
|
||||
func (p ProviderVerifierOptions) toOIDCConfig() *oidc.Config {
|
||||
return &oidc.Config{
|
||||
ClientID: p.ClientID,
|
||||
SkipIssuerCheck: p.SkipIssuerVerification,
|
||||
SkipClientIDCheck: true,
|
||||
ClientID: p.ClientID,
|
||||
SkipIssuerCheck: p.SkipIssuerVerification,
|
||||
SkipClientIDCheck: true,
|
||||
SupportedSigningAlgs: p.SupportedSigningAlgs,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,21 +117,25 @@ type verifierBuilder func(*oidc.Config) *oidc.IDTokenVerifier
|
||||
func getVerifierBuilder(ctx context.Context, opts ProviderVerifierOptions) (verifierBuilder, DiscoveryProvider, error) {
|
||||
if opts.SkipDiscovery {
|
||||
// Instead of discovering the JWKs URK, it needs to be specified in the opts already
|
||||
return newVerifierBuilder(ctx, opts.IssuerURL, opts.JWKsURL), nil, nil
|
||||
return newVerifierBuilder(ctx, opts.IssuerURL, opts.JWKsURL, opts.SupportedSigningAlgs), nil, nil
|
||||
}
|
||||
|
||||
provider, err := NewProvider(ctx, opts.IssuerURL, opts.SkipIssuerVerification)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("error while discovery OIDC configuration: %v", err)
|
||||
}
|
||||
verifierBuilder := newVerifierBuilder(ctx, opts.IssuerURL, provider.Endpoints().JWKsURL)
|
||||
verifierBuilder := newVerifierBuilder(ctx, opts.IssuerURL, provider.Endpoints().JWKsURL, provider.SupportedSigningAlgs())
|
||||
return verifierBuilder, provider, nil
|
||||
}
|
||||
|
||||
// newVerifierBuilder returns a function to create a IDToken verifier from an OIDC config.
|
||||
func newVerifierBuilder(ctx context.Context, issuerURL, jwksURL string) verifierBuilder {
|
||||
func newVerifierBuilder(ctx context.Context, issuerURL, jwksURL string, supportedSigningAlgs []string) verifierBuilder {
|
||||
keySet := oidc.NewRemoteKeySet(ctx, jwksURL)
|
||||
return func(oidcConfig *oidc.Config) *oidc.IDTokenVerifier {
|
||||
if len(supportedSigningAlgs) > 0 {
|
||||
oidcConfig.SupportedSigningAlgs = supportedSigningAlgs
|
||||
}
|
||||
|
||||
return oidc.NewVerifier(issuerURL, keySet, oidcConfig)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"github.com/golang-jwt/jwt"
|
||||
"github.com/oauth2-proxy/mockoidc"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/ginkgo/extensions/table"
|
||||
|
||||
Reference in New Issue
Block a user