feat: add support for specifying allowed OIDC JWT signing algorithms (#2753) (#2851)

* feat: add support for specifying allowed OIDC JWT signing algorithms (#2753)

TODO:
- [X] update docs
- [X] add support in yaml (modern) config
- [X] add more test(s)?

Add (legacy for now) configuration flag "oidc-enabled-signing-alg" (cfg:
oidc_enabled_signing_algs) that allows setting what signing algorithms
are specified by provider in JWT header ("alg" header claim).

In particular useful when skip_oidc_discovery = true, as verifier
defaults to only accept "RS256" in alg field in such circumstances.

Signed-off-by: Jan Larwig <jan@larwig.com>

* doc: update changelog and alpha config

Signed-off-by: Jan Larwig <jan@larwig.com>

* feat: add signing algorithm intersection handling with oidc discovery and additional tests

Signed-off-by: Jan Larwig <jan@larwig.com>

---------

Signed-off-by: Jan Larwig <jan@larwig.com>
Co-authored-by: Jan Larwig <jan@larwig.com>
This commit is contained in:
andoks
2026-03-18 22:24:27 +08:00
committed by GitHub
co-authored by Jan Larwig
parent 30853098c7
commit 7c96234233
14 changed files with 363 additions and 11 deletions
+33
View File
@@ -4,10 +4,27 @@ import (
"fmt"
"os"
jose "github.com/go-jose/go-jose/v4"
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/util/ptr"
)
var supportedOIDCSigningAlgorithms = map[jose.SignatureAlgorithm]struct{}{
jose.EdDSA: {},
jose.HS256: {},
jose.HS384: {},
jose.HS512: {},
jose.RS256: {},
jose.RS384: {},
jose.RS512: {},
jose.ES256: {},
jose.ES384: {},
jose.ES512: {},
jose.PS256: {},
jose.PS384: {},
jose.PS512: {},
}
// validateProviders is the initial validation migration for multiple providrers
// It currently includes only logic that can verify the providers one by one and does not break the valdation pipe
func validateProviders(o *options.Options) []string {
@@ -59,6 +76,22 @@ func validateProvider(provider options.Provider, providerIDs map[string]struct{}
msgs = append(msgs, validateEntraConfig(provider)...)
}
msgs = append(msgs, validateOIDCSigningAlgorithms(provider)...)
return msgs
}
func validateOIDCSigningAlgorithms(provider options.Provider) []string {
msgs := []string{}
for _, algorithm := range provider.OIDCConfig.EnabledSigningAlgs {
if _, ok := supportedOIDCSigningAlgorithms[jose.SignatureAlgorithm(algorithm)]; ok {
continue
}
msgs = append(msgs, fmt.Sprintf("provider %s has invalid EnabledSigningAlgs entry %q", provider.ID, algorithm))
}
return msgs
}
+53
View File
@@ -18,6 +18,33 @@ var _ = Describe("Providers", func() {
ClientSecret: "ClientSecret",
}
validOIDCSigningAlgorithmsProvider := options.Provider{
ID: "ProviderIDOIDCSigningAlgorithms",
ClientID: "ClientID",
ClientSecret: "ClientSecret",
OIDCConfig: options.OIDCOptions{
EnabledSigningAlgs: []string{"RS256", "EdDSA"},
},
}
invalidOIDCSigningAlgorithmsProvider := options.Provider{
ID: "ProviderIDInvalidOIDCSigningAlgorithms",
ClientID: "ClientID",
ClientSecret: "ClientSecret",
OIDCConfig: options.OIDCOptions{
EnabledSigningAlgs: []string{"RS256", "invalid"},
},
}
invalidOIDCSigningAlgorithmCaseProvider := options.Provider{
ID: "ProviderIDInvalidOIDCSigningAlgorithmCase",
ClientID: "ClientID",
ClientSecret: "ClientSecret",
OIDCConfig: options.OIDCOptions{
EnabledSigningAlgs: []string{"rs256"},
},
}
validLoginGovProvider := options.Provider{
Type: "login.gov",
ID: "ProviderIDLoginGov",
@@ -34,6 +61,8 @@ var _ = Describe("Providers", func() {
emptyIDMsg := "provider has empty id: ids are required for all providers"
duplicateProviderIDMsg := "multiple providers found with id ProviderID: provider ids must be unique"
skipButtonAndMultipleProvidersMsg := "SkipProviderButton and multiple providers are mutually exclusive"
invalidOIDCSigningAlgorithmMsg := "provider ProviderIDInvalidOIDCSigningAlgorithms has invalid EnabledSigningAlgs entry \"invalid\""
invalidOIDCSigningAlgorithmCaseMsg := "provider ProviderIDInvalidOIDCSigningAlgorithmCase has invalid EnabledSigningAlgs entry \"rs256\""
DescribeTable("validateProviders",
func(o *validateProvidersTableInput) {
@@ -79,5 +108,29 @@ var _ = Describe("Providers", func() {
},
errStrings: []string{skipButtonAndMultipleProvidersMsg},
}),
Entry("with valid OIDC signing algorithms", &validateProvidersTableInput{
options: &options.Options{
Providers: options.Providers{
validOIDCSigningAlgorithmsProvider,
},
},
errStrings: []string{},
}),
Entry("with an invalid OIDC signing algorithm", &validateProvidersTableInput{
options: &options.Options{
Providers: options.Providers{
invalidOIDCSigningAlgorithmsProvider,
},
},
errStrings: []string{invalidOIDCSigningAlgorithmMsg},
}),
Entry("with an OIDC signing algorithm using invalid casing", &validateProvidersTableInput{
options: &options.Options{
Providers: options.Providers{
invalidOIDCSigningAlgorithmCaseProvider,
},
},
errStrings: []string{invalidOIDCSigningAlgorithmCaseMsg},
}),
)
})