Files
wonderwall/pkg/openid/config/provider.go
Trong Huu Nguyen 1906024da0 feat(openid/acr): remove old values and backward compatibility for new idporten
We no longer expect nor accept tokens with old acr values during
validation as ID-porten no longer issues tokens with these values.

This also removes backward compatibility in cases where configured
values targeted the new ID-porten while using old ID-porten.

We still maintain an internal mapping from old values to new values
for forward compatibilty when using old values provided in the login
parameter and the `openid.acr-values` flag.
2024-06-27 12:34:16 +02:00

177 lines
5.4 KiB
Go

package config
import (
"encoding/json"
"fmt"
"net/http"
"net/url"
log "github.com/sirupsen/logrus"
"github.com/nais/wonderwall/pkg/config"
"github.com/nais/wonderwall/pkg/openid/acr"
)
type Provider interface {
AuthorizationEndpoint() string
EndSessionEndpointURL() url.URL
Issuer() string
JwksURI() string
TokenEndpoint() string
ACRValuesSupported() Supported
UILocalesSupported() Supported
SessionStateRequired() bool
SidClaimRequired() bool
}
type provider struct {
endSessionEndpointURL *url.URL
metadata *ProviderMetadata
}
func (p *provider) AuthorizationEndpoint() string {
return p.metadata.AuthorizationEndpoint
}
func (p *provider) EndSessionEndpointURL() url.URL {
return *p.endSessionEndpointURL
}
func (p *provider) TokenEndpoint() string {
return p.metadata.TokenEndpoint
}
func (p *provider) Issuer() string {
return p.metadata.Issuer
}
func (p *provider) JwksURI() string {
return p.metadata.JwksURI
}
func (p *provider) ACRValuesSupported() Supported {
return p.metadata.ACRValuesSupported
}
func (p *provider) UILocalesSupported() Supported {
return p.metadata.UILocalesSupported
}
func (p *provider) SessionStateRequired() bool {
return len(p.metadata.CheckSessionIframe) > 0
}
func (p *provider) SidClaimRequired() bool {
return p.metadata.FrontchannelLogoutSupported && p.metadata.FrontchannelLogoutSessionSupported
}
func NewProviderConfig(cfg *config.Config) (Provider, error) {
response, err := http.Get(cfg.OpenID.WellKnownURL)
if err != nil {
return nil, fmt.Errorf("fetching well known configuration: %w", err)
}
defer response.Body.Close()
providerCfg := new(ProviderMetadata)
if err := json.NewDecoder(response.Body).Decode(providerCfg); err != nil {
return nil, fmt.Errorf("decoding well known configuration: %w", err)
}
err = providerCfg.Validate(cfg.OpenID)
if err != nil {
return nil, fmt.Errorf("validating well known configuration: %w", err)
}
endSessionEndpointURL, err := url.Parse(providerCfg.EndSessionEndpoint)
if err != nil {
return nil, fmt.Errorf("parsing end session endpoint URL: %w", err)
}
providerCfg.Print()
return &provider{
endSessionEndpointURL: endSessionEndpointURL,
metadata: providerCfg,
}, nil
}
type ProviderMetadata struct {
Issuer string `json:"issuer"`
AuthorizationEndpoint string `json:"authorization_endpoint"`
PushedAuthorizationRequestEndpoint string `json:"pushed_authorization_request_endpoint"`
TokenEndpoint string `json:"token_endpoint"`
EndSessionEndpoint string `json:"end_session_endpoint"`
RevocationEndpoint string `json:"revocation_endpoint"`
JwksURI string `json:"jwks_uri"`
ResponseTypesSupported []string `json:"response_types_supported"`
ResponseModesSupported []string `json:"response_modes_supported"`
SubjectTypesSupported []string `json:"subject_types_supported"`
IDTokenSigningAlgValuesSupported []string `json:"id_token_signing_alg_values_supported"`
CodeChallengeMethodsSupported []string `json:"code_challenge_methods_supported"`
UserInfoEndpoint string `json:"userinfo_endpoint"`
ScopesSupported []string `json:"scopes_supported"`
UILocalesSupported Supported `json:"ui_locales_supported"`
ACRValuesSupported Supported `json:"acr_values_supported"`
FrontchannelLogoutSupported bool `json:"frontchannel_logout_supported"`
FrontchannelLogoutSessionSupported bool `json:"frontchannel_logout_session_supported"`
IntrospectionEndpoint string `json:"introspection_endpoint"`
TokenEndpointAuthMethodsSupported []string `json:"token_endpoint_auth_methods_supported"`
RequestParameterSupported bool `json:"request_parameter_supported"`
RequestURIParameterSupported bool `json:"request_uri_parameter_supported"`
RequestObjectSigningAlgValuesSupported []string `json:"request_object_signing_alg_values_supported"`
CheckSessionIframe string `json:"check_session_iframe"`
}
func (c *ProviderMetadata) Print() {
log.WithField("logger", "wonderwall.config").
Debugf("openid provider config: %+v", c)
}
func (c *ProviderMetadata) Validate(cfg config.OpenID) error {
err := c.validateAcrValues(cfg.ACRValues)
if err != nil {
return err
}
err = c.validateLocaleValues(cfg.UILocales)
if err != nil {
return err
}
return nil
}
func (c *ProviderMetadata) validateAcrValues(acrValue string) error {
if len(acrValue) == 0 || c.ACRValuesSupported.Contains(acrValue) {
return nil
}
translatedAcr, ok := acr.IDPortenLegacyMapping[acrValue]
if ok && c.ACRValuesSupported.Contains(translatedAcr) {
return nil
}
return fmt.Errorf("identity provider does not support '%s=%s', must be one of %s", config.OpenIDACRValues, acrValue, c.ACRValuesSupported)
}
func (c *ProviderMetadata) validateLocaleValues(locale string) error {
if len(locale) == 0 || c.UILocalesSupported.Contains(locale) {
return nil
}
return fmt.Errorf("identity provider does not support '%s=%s', must be one of %s", config.OpenIDUILocales, locale, c.UILocalesSupported)
}
type Supported []string
func (in Supported) Contains(value string) bool {
for _, allowed := range in {
if allowed == value {
return true
}
}
return false
}