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.
This commit is contained in:
Trong Huu Nguyen
2026-08-10 12:30:25 +02:00
parent 90ceca2382
commit f3f76fccc8
7 changed files with 24 additions and 23 deletions
+1 -1
View File
@@ -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`. |
+5 -5
View File
@@ -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'.")
+1 -1
View File
@@ -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"},
+1 -1
View File
@@ -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()
}
+11 -10
View File
@@ -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
+3 -3
View File
@@ -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)
+2 -2
View File
@@ -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)
}