From f3f76fccc889830d9629232192ddb3af4a0c7f73 Mon Sep 17 00:00:00 2001 From: Trong Huu Nguyen Date: Mon, 10 Aug 2026 12:30:25 +0200 Subject: [PATCH] refactor(config)!: rename id-token-signing-alg to jwks-fallback-alg The flag never described id_token verification. It assigns an algorithm to provider JWKS keys that omit "alg", which is a compatibility shim for providers such as Azure and ID-porten. BREAKING CHANGE: openid.id-token-signing-alg is now openid.jwks-fallback-alg. A config file with the old key fails to start. The old environment variable is ignored and the value falls back to RS256. --- docs/configuration.md | 2 +- pkg/config/openid.go | 10 +++++----- pkg/mock/config.go | 2 +- pkg/mock/provider.go | 2 +- pkg/openid/config/provider.go | 21 +++++++++++---------- pkg/openid/config/provider_test.go | 6 +++--- pkg/openid/provider/provider.go | 4 ++-- 7 files changed, 24 insertions(+), 23 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index ead5419..3ec9d91 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -37,7 +37,7 @@ The following flags are available: | `openid.client-jwk` | string | | JWK containing the private key for the OpenID client in string format. If configured, this takes precedence over `openid.client-secret`. | | `openid.client-secret` | string | | Client secret for the OpenID client. Overridden by `openid.client-jwk`, if configured. | | `openid.domain-hint` | string | | Domain hint to include in authorization request for IdPs that support this parameter (e.g. Entra ID). | -| `openid.id-token-signing-alg` | string | `RS256` | Expected JWA value (as defined in RFC 7518) of public keys for validating id_token signatures. This only applies where the key's `alg` header is not set. | +| `openid.jwks-fallback-alg` | string | `RS256` | JWA value (as defined in RFC 7518) to assign to provider JWKS keys when their `alg` header is not set. | | `openid.new-client-auth-jwt-type` | bool | `false` | When enabled, sets the value of the \"typ\" header of the JWT used for client authentication equal to "client-authentication+jwt" in accordance with RFC7523bis. If not enabled, the value is set to "JWT". | | `openid.post-logout-redirect-uri` | string | | URI for redirecting the user after successful logout at the Identity Provider. | | `openid.provider` | string | `openid` | Provider configuration to load and use, either `openid`, `azure`, `idporten`. | diff --git a/pkg/config/openid.go b/pkg/config/openid.go index 4fa685e..c0a9543 100644 --- a/pkg/config/openid.go +++ b/pkg/config/openid.go @@ -25,7 +25,7 @@ type OpenID struct { ClientJWK string `json:"client-jwk"` ClientSecret string `json:"client-secret"` DomainHint string `json:"domain-hint"` - IDTokenSigningAlg string `json:"id-token-signing-alg"` + JWKSFallbackAlg string `json:"jwks-fallback-alg"` NewClientAuthJWTType bool `json:"new-client-auth-jwt-type"` PostLogoutRedirectURI string `json:"post-logout-redirect-uri"` Provider Provider `json:"provider"` @@ -46,9 +46,9 @@ func (in OpenID) TrustedAudiences() map[string]bool { } func (in OpenID) Validate() error { - _, ok := jwa.LookupSignatureAlgorithm(in.IDTokenSigningAlg) + _, ok := jwa.LookupSignatureAlgorithm(in.JWKSFallbackAlg) if !ok { - return fmt.Errorf("invalid id_token signing algorithm: %q, must be one of %s", in.IDTokenSigningAlg, jwa.SignatureAlgorithms()) + return fmt.Errorf("invalid JWKS fallback algorithm: %q, must be one of %s", in.JWKSFallbackAlg, jwa.SignatureAlgorithms()) } return nil @@ -61,8 +61,8 @@ const ( OpenIDClientJWK = "openid.client-jwk" OpenIDClientSecret = "openid.client-secret" // #nosec G101 -- configuration key, not a credential OpenIDDomainHint = "openid.domain-hint" + OpenIDJWKSFallbackAlg = "openid.jwks-fallback-alg" OpenIDNewClientAuthJWTType = "openid.new-client-auth-jwt-type" - OpenIDIDTokenSigningAlg = "openid.id-token-signing-alg" // #nosec G101 -- configuration key, not a credential OpenIDPostLogoutRedirectURI = "openid.post-logout-redirect-uri" OpenIDProvider = "openid.provider" OpenIDResourceIndicator = "openid.resource-indicator" @@ -78,7 +78,7 @@ func openidFlags() { flag.String(OpenIDClientJWK, "", "JWK containing the private key for the OpenID client in string format. If configured, this takes precedence over 'openid.client-secret'.") flag.String(OpenIDClientSecret, "", "Client secret for the OpenID client. Overridden by 'openid.client-jwk', if configured.") flag.String(OpenIDDomainHint, "", "Domain hint to include in authorization request for IdPs that support this parameter (e.g. Entra ID).") - flag.String(OpenIDIDTokenSigningAlg, jwa.RS256().String(), "Expected JWA value (as defined in RFC 7518) of public keys for validating id_token signatures. This only applies where the key's 'alg' header is not set.") + flag.String(OpenIDJWKSFallbackAlg, jwa.RS256().String(), "JWA value (as defined in RFC 7518) to assign to provider JWKS keys when their 'alg' header is not set.") flag.Bool(OpenIDNewClientAuthJWTType, false, "When enabled, sets the value of the \"typ\" header of the JWT used for client authentication equal to \"client-authentication+jwt\" in accordance with RFC7523bis. If not enabled, the value is set to \"JWT\".") flag.String(OpenIDPostLogoutRedirectURI, "", "URI for redirecting the user after successful logout at the Identity Provider.") flag.String(OpenIDProvider, string(ProviderOpenID), "Provider configuration to load and use, either 'openid', 'azure', 'idporten'.") diff --git a/pkg/mock/config.go b/pkg/mock/config.go index e83d1a1..d71eb0c 100644 --- a/pkg/mock/config.go +++ b/pkg/mock/config.go @@ -19,7 +19,7 @@ func Config() *config.Config { OpenID: config.OpenID{ ACRValues: "idporten-loa-high", ClientID: "client-id", - IDTokenSigningAlg: "RS256", + JWKSFallbackAlg: "RS256", PostLogoutRedirectURI: "https://google.com", Provider: "test", Scopes: []string{"some-scope"}, diff --git a/pkg/mock/provider.go b/pkg/mock/provider.go index ba91d6b..62041d7 100644 --- a/pkg/mock/provider.go +++ b/pkg/mock/provider.go @@ -58,7 +58,7 @@ func (t *TestProviderConfiguration) EndSessionEndpointURL() url.URL { return *u } -func (t *TestProviderConfiguration) IDTokenSigningAlg() jwa.KeyAlgorithm { +func (t *TestProviderConfiguration) JwksFallbackAlg() jwa.KeyAlgorithm { return jwa.RS256() } diff --git a/pkg/openid/config/provider.go b/pkg/openid/config/provider.go index de5f77e..b9a0c27 100644 --- a/pkg/openid/config/provider.go +++ b/pkg/openid/config/provider.go @@ -22,7 +22,7 @@ type Provider interface { AuthorizationEndpoint() string AuthorizationResponseIssParameterSupported() bool EndSessionEndpointURL() url.URL - IDTokenSigningAlg() jwa.KeyAlgorithm + JwksFallbackAlg() jwa.KeyAlgorithm Issuer() string JwksURI() string PushedAuthorizationRequestEndpoint() string @@ -34,7 +34,7 @@ type Provider interface { type provider struct { endSessionEndpointURL *url.URL - idTokenSigningAlg jwa.KeyAlgorithm + jwksFallbackAlg jwa.KeyAlgorithm metadata *ProviderMetadata } @@ -54,8 +54,8 @@ func (p *provider) TokenEndpoint() string { return p.metadata.TokenEndpoint } -func (p *provider) IDTokenSigningAlg() jwa.KeyAlgorithm { - return p.idTokenSigningAlg +func (p *provider) JwksFallbackAlg() jwa.KeyAlgorithm { + return p.jwksFallbackAlg } func (p *provider) Issuer() string { @@ -127,14 +127,14 @@ func NewProviderConfig(ctx context.Context, cfg *config.Config) (Provider, error providerCfg.Print() - signingAlg, ok := jwa.LookupSignatureAlgorithm(cfg.OpenID.IDTokenSigningAlg) + fallbackAlg, ok := jwa.LookupSignatureAlgorithm(cfg.OpenID.JWKSFallbackAlg) if !ok { - return nil, fmt.Errorf("invalid id_token signing algorithm: %q, must be one of %s", cfg.OpenID.IDTokenSigningAlg, jwa.SignatureAlgorithms()) + return nil, fmt.Errorf("invalid JWKS fallback algorithm: %q, must be one of %s", cfg.OpenID.JWKSFallbackAlg, jwa.SignatureAlgorithms()) } return &provider{ endSessionEndpointURL: endSessionEndpointURL, - idTokenSigningAlg: signingAlg, + jwksFallbackAlg: fallbackAlg, metadata: providerCfg, }, nil } @@ -183,7 +183,7 @@ func (c *ProviderMetadata) Validate(cfg config.OpenID) error { return err } - err = c.validateIDTokenSigningAlg(cfg.IDTokenSigningAlg) + err = c.validateJWKSFallbackAlg(cfg.JWKSFallbackAlg) if err != nil { return err } @@ -212,12 +212,13 @@ func (c *ProviderMetadata) validateLocaleValues(locale string) error { return fmt.Errorf("identity provider does not support '%s=%s', must be one of %s", config.OpenIDUILocales, locale, c.UILocalesSupported) } -func (c *ProviderMetadata) validateIDTokenSigningAlg(algorithm string) error { +func (c *ProviderMetadata) validateJWKSFallbackAlg(algorithm string) error { + // The JWKS is only used to verify id_tokens, so this is the right list. if slices.Contains(c.IDTokenSigningAlgValuesSupported, algorithm) { return nil } - return fmt.Errorf("identity provider does not support '%s=%s', must be one of %s", config.OpenIDIDTokenSigningAlg, algorithm, c.IDTokenSigningAlgValuesSupported) + return fmt.Errorf("identity provider does not support '%s=%s', must be one of %s", config.OpenIDJWKSFallbackAlg, algorithm, c.IDTokenSigningAlgValuesSupported) } type Supported []string diff --git a/pkg/openid/config/provider_test.go b/pkg/openid/config/provider_test.go index d8a1499..dccb3dd 100644 --- a/pkg/openid/config/provider_test.go +++ b/pkg/openid/config/provider_test.go @@ -49,7 +49,7 @@ func TestProviderMetadata_Validate(t *testing.T) { }, { name: "invalid signing algorithm", - config: config.OpenID{IDTokenSigningAlg: "HS256"}, + config: config.OpenID{JWKSFallbackAlg: "HS256"}, assertion: assert.Error, }, } { @@ -61,8 +61,8 @@ func TestProviderMetadata_Validate(t *testing.T) { if tt.config.UILocales != "" { cfg.OpenID.UILocales = tt.config.UILocales } - if tt.config.IDTokenSigningAlg != "" { - cfg.OpenID.IDTokenSigningAlg = tt.config.IDTokenSigningAlg + if tt.config.JWKSFallbackAlg != "" { + cfg.OpenID.JWKSFallbackAlg = tt.config.JWKSFallbackAlg } err := metadata.Validate(cfg.OpenID) diff --git a/pkg/openid/provider/provider.go b/pkg/openid/provider/provider.go index ce9f576..01e916a 100644 --- a/pkg/openid/provider/provider.go +++ b/pkg/openid/provider/provider.go @@ -36,7 +36,7 @@ func (p *JwksProvider) GetPublicJwkSet(ctx context.Context) (*jwk.Set, error) { return nil, fmt.Errorf("provider: fetching jwks: %w", err) } - set, err = ensureJwkSetWithAlg(set, p.config.IDTokenSigningAlg()) + set, err = ensureJwkSetWithAlg(set, p.config.JwksFallbackAlg()) if err != nil { return nil, fmt.Errorf("provider: mutating jwks: %w", err) } @@ -65,7 +65,7 @@ func (p *JwksProvider) RefreshPublicJwkSet(ctx context.Context) (*jwk.Set, error return nil, fmt.Errorf("provider: refreshing jwks: %w", err) } - set, err = ensureJwkSetWithAlg(set, p.config.IDTokenSigningAlg()) + set, err = ensureJwkSetWithAlg(set, p.config.JwksFallbackAlg()) if err != nil { return nil, fmt.Errorf("provider: mutating jwks: %w", err) }