diff --git a/management/cmd/management.go b/management/cmd/management.go index 5058447d8..383991928 100644 --- a/management/cmd/management.go +++ b/management/cmd/management.go @@ -396,6 +396,12 @@ func loadMgmtConfig(mgmtConfigPath string) (*server.Config, error) { } log.Infof("loaded OIDC configuration from the provided IDP configuration endpoint: %s", oidcEndpoint) + log.Infof("configuring IdpManagerConfig.OIDCConfig.Issuer with a new value %s,", oidcConfig.Issuer) + config.IdpManagerConfig.OIDCConfig.Issuer = strings.TrimRight(oidcConfig.Issuer, "/") + + log.Infof("configuring IdpManagerConfig.OIDCConfig.TokenEndpoint with a new value %s,", oidcConfig.TokenEndpoint) + config.IdpManagerConfig.OIDCConfig.TokenEndpoint = oidcConfig.TokenEndpoint + log.Infof("overriding HttpConfig.AuthIssuer with a new value %s, previously configured value: %s", oidcConfig.Issuer, config.HttpConfig.AuthIssuer) config.HttpConfig.AuthIssuer = oidcConfig.Issuer @@ -441,7 +447,7 @@ type OIDCConfigResponse struct { func fetchOIDCConfig(oidcEndpoint string) (OIDCConfigResponse, error) { res, err := http.Get(oidcEndpoint) if err != nil { - return OIDCConfigResponse{}, fmt.Errorf("failed fetching OIDC configuration fro mendpoint %s %v", oidcEndpoint, err) + return OIDCConfigResponse{}, fmt.Errorf("failed fetching OIDC configuration from endpoint %s %v", oidcEndpoint, err) } defer func() { diff --git a/management/server/idp/auth0.go b/management/server/idp/auth0.go index bb4d3e321..1bd64bbc6 100644 --- a/management/server/idp/auth0.go +++ b/management/server/idp/auth0.go @@ -32,10 +32,10 @@ type Auth0Manager struct { // Auth0ClientConfig auth0 manager client configurations type Auth0ClientConfig struct { Audience string - AuthIssuer string + AuthIssuer string `json:"-"` ClientID string ClientSecret string - GrantType string + GrantType string `json:"-"` } // auth0JWTRequest payload struct to request a JWT Token @@ -110,7 +110,8 @@ type auth0Profile struct { } // NewAuth0Manager creates a new instance of the Auth0Manager -func NewAuth0Manager(config Auth0ClientConfig, appMetrics telemetry.AppMetrics) (*Auth0Manager, error) { +func NewAuth0Manager(oidcConfig OIDCConfig, config Auth0ClientConfig, + appMetrics telemetry.AppMetrics) (*Auth0Manager, error) { httpTransport := http.DefaultTransport.(*http.Transport).Clone() httpTransport.MaxIdleConns = 5 @@ -121,17 +122,19 @@ func NewAuth0Manager(config Auth0ClientConfig, appMetrics telemetry.AppMetrics) } helper := JsonParser{} + config.AuthIssuer = oidcConfig.TokenEndpoint + config.GrantType = "client_credentials" - if config.ClientID == "" || config.ClientSecret == "" || config.GrantType == "" || config.Audience == "" || config.AuthIssuer == "" { - return nil, fmt.Errorf("auth0 idp configuration is not complete") + if config.ClientID == "" { + return nil, fmt.Errorf("auth0 IdP configuration is incomplete, clientID is missing") } - if config.GrantType != "client_credentials" { - return nil, fmt.Errorf("auth0 idp configuration failed. Grant Type should be client_credentials") + if config.ClientSecret == "" { + return nil, fmt.Errorf("auth0 IdP configuration is incomplete, ClientSecret is missing") } - if !strings.HasPrefix(strings.ToLower(config.AuthIssuer), "https://") { - return nil, fmt.Errorf("auth0 idp configuration failed. AuthIssuer should contain https://") + if config.Audience == "" { + return nil, fmt.Errorf("auth0 IdP configuration is incomplete, Audience is missing") } credentials := &Auth0Credentials{ diff --git a/management/server/idp/auth0_test.go b/management/server/idp/auth0_test.go index 75ff1d080..b4a5cbf20 100644 --- a/management/server/idp/auth0_test.go +++ b/management/server/idp/auth0_test.go @@ -459,26 +459,9 @@ func TestNewAuth0Manager(t *testing.T) { testCase3Config := defaultTestConfig testCase3Config.AuthIssuer = "abc-auth0.eu.auth0.com" - testCase3 := test{ - name: "Wrong Auth Issuer Format", - inputConfig: testCase3Config, - assertErrFunc: require.Error, - assertErrFuncMessage: "should return error when wrong auth issuer format", - } - - testCase4Config := defaultTestConfig - testCase4Config.GrantType = "spa" - - testCase4 := test{ - name: "Wrong Grant Type", - inputConfig: testCase4Config, - assertErrFunc: require.Error, - assertErrFuncMessage: "should return error when wrong grant type", - } - - for _, testCase := range []test{testCase1, testCase2, testCase3, testCase4} { + for _, testCase := range []test{testCase1, testCase2} { t.Run(testCase.name, func(t *testing.T) { - _, err := NewAuth0Manager(testCase.inputConfig, &telemetry.MockAppMetrics{}) + _, err := NewAuth0Manager(OIDCConfig{}, testCase.inputConfig, &telemetry.MockAppMetrics{}) testCase.assertErrFunc(t, err, testCase.assertErrFuncMessage) }) } diff --git a/management/server/idp/azure.go b/management/server/idp/azure.go index 57b669fc3..3b9d2ff38 100644 --- a/management/server/idp/azure.go +++ b/management/server/idp/azure.go @@ -37,12 +37,13 @@ type AzureManager struct { // AzureClientConfig azure manager client configurations. type AzureClientConfig struct { - ClientID string - ClientSecret string - GraphAPIEndpoint string - ObjectID string - TokenEndpoint string - GrantType string + ClientID string + ClientSecret string + ObjectID string + + GraphAPIEndpoint string `json:"-"` + TokenEndpoint string `json:"-"` + GrantType string `json:"-"` } // AzureCredentials azure authentication information. @@ -74,7 +75,8 @@ type azureExtension struct { } // NewAzureManager creates a new instance of the AzureManager. -func NewAzureManager(config AzureClientConfig, appMetrics telemetry.AppMetrics) (*AzureManager, error) { +func NewAzureManager(oidcConfig OIDCConfig, config AzureClientConfig, + appMetrics telemetry.AppMetrics) (*AzureManager, error) { httpTransport := http.DefaultTransport.(*http.Transport).Clone() httpTransport.MaxIdleConns = 5 @@ -84,13 +86,20 @@ func NewAzureManager(config AzureClientConfig, appMetrics telemetry.AppMetrics) } helper := JsonParser{} + config.TokenEndpoint = oidcConfig.TokenEndpoint + config.GraphAPIEndpoint = "https://graph.microsoft.com" + config.GrantType = "client_credentials" - if config.ClientID == "" || config.ClientSecret == "" || config.GrantType == "" || config.GraphAPIEndpoint == "" || config.TokenEndpoint == "" { - return nil, fmt.Errorf("azure idp configuration is not complete") + if config.ClientID == "" { + return nil, fmt.Errorf("azure IdP configuration is incomplete, clientID is missing") } - if config.GrantType != "client_credentials" { - return nil, fmt.Errorf("azure idp configuration failed. Grant Type should be client_credentials") + if config.ClientSecret == "" { + return nil, fmt.Errorf("azure IdP configuration is incomplete, ClientSecret is missing") + } + + if config.ObjectID == "" { + return nil, fmt.Errorf("azure IdP configuration is incomplete, ObjectID is missing") } credentials := &AzureCredentials{ diff --git a/management/server/idp/idp.go b/management/server/idp/idp.go index c191f45fe..876680e27 100644 --- a/management/server/idp/idp.go +++ b/management/server/idp/idp.go @@ -19,9 +19,17 @@ type Manager interface { GetUserByEmail(email string) ([]*UserData, error) } +// OIDCConfig specifies configuration for OpenID Connect provider +// These configurations are automatically loaded from the OIDC endpoint +type OIDCConfig struct { + Issuer string + TokenEndpoint string +} + // Config an idp configuration struct to be loaded from management server's config file type Config struct { ManagerType string + OIDCConfig OIDCConfig `json:"-"` Auth0ClientCredentials Auth0ClientConfig AzureClientCredentials AzureClientConfig KeycloakClientCredentials KeycloakClientConfig @@ -74,13 +82,13 @@ func NewManager(config Config, appMetrics telemetry.AppMetrics) (Manager, error) case "none", "": return nil, nil case "auth0": - return NewAuth0Manager(config.Auth0ClientCredentials, appMetrics) + return NewAuth0Manager(config.OIDCConfig, config.Auth0ClientCredentials, appMetrics) case "azure": - return NewAzureManager(config.AzureClientCredentials, appMetrics) + return NewAzureManager(config.OIDCConfig, config.AzureClientCredentials, appMetrics) case "keycloak": - return NewKeycloakManager(config.KeycloakClientCredentials, appMetrics) + return NewKeycloakManager(config.OIDCConfig, config.KeycloakClientCredentials, appMetrics) case "zitadel": - return NewZitadelManager(config.ZitadelClientCredentials, appMetrics) + return NewZitadelManager(config.OIDCConfig, config.ZitadelClientCredentials, appMetrics) default: return nil, fmt.Errorf("invalid manager type: %s", config.ManagerType) } diff --git a/management/server/idp/keycloak.go b/management/server/idp/keycloak.go index a0c004db2..8d35640be 100644 --- a/management/server/idp/keycloak.go +++ b/management/server/idp/keycloak.go @@ -37,8 +37,8 @@ type KeycloakClientConfig struct { ClientID string ClientSecret string AdminEndpoint string - TokenEndpoint string - GrantType string + TokenEndpoint string `json:"-"` + GrantType string `json:"-"` } // KeycloakCredentials keycloak authentication information. @@ -82,7 +82,8 @@ type keycloakProfile struct { } // NewKeycloakManager creates a new instance of the KeycloakManager. -func NewKeycloakManager(config KeycloakClientConfig, appMetrics telemetry.AppMetrics) (*KeycloakManager, error) { +func NewKeycloakManager(oidcConfig OIDCConfig, config KeycloakClientConfig, + appMetrics telemetry.AppMetrics) (*KeycloakManager, error) { httpTransport := http.DefaultTransport.(*http.Transport).Clone() httpTransport.MaxIdleConns = 5 @@ -92,13 +93,19 @@ func NewKeycloakManager(config KeycloakClientConfig, appMetrics telemetry.AppMet } helper := JsonParser{} + config.TokenEndpoint = oidcConfig.TokenEndpoint + config.GrantType = "client_credentials" - if config.ClientID == "" || config.ClientSecret == "" || config.GrantType == "" || config.AdminEndpoint == "" || config.TokenEndpoint == "" { - return nil, fmt.Errorf("keycloak idp configuration is not complete") + if config.ClientID == "" { + return nil, fmt.Errorf("keycloak IdP configuration is incomplete, clientID is missing") } - if config.GrantType != "client_credentials" { - return nil, fmt.Errorf("keycloak idp configuration failed. Grant Type should be client_credentials") + if config.ClientSecret == "" { + return nil, fmt.Errorf("keycloak IdP configuration is incomplete, ClientSecret is missing") + } + + if config.AdminEndpoint == "" { + return nil, fmt.Errorf("keycloak IdP configuration is incomplete, AdminEndpoint is missing") } credentials := &KeycloakCredentials{ diff --git a/management/server/idp/keycloak_test.go b/management/server/idp/keycloak_test.go index 141937c62..589dc4cd9 100644 --- a/management/server/idp/keycloak_test.go +++ b/management/server/idp/keycloak_test.go @@ -46,19 +46,19 @@ func TestNewKeycloakManager(t *testing.T) { assertErrFuncMessage: "should return error when field empty", } - testCase5Config := defaultTestConfig - testCase5Config.GrantType = "authorization_code" + testCase3Config := defaultTestConfig + testCase3Config.ClientSecret = "" - testCase5 := test{ - name: "Wrong GrantType", - inputConfig: testCase5Config, + testCase3 := test{ + name: "Missing ClientSecret Configuration", + inputConfig: testCase3Config, assertErrFunc: require.Error, - assertErrFuncMessage: "should return error when wrong grant type", + assertErrFuncMessage: "should return error when field empty", } - for _, testCase := range []test{testCase1, testCase2, testCase5} { + for _, testCase := range []test{testCase1, testCase2, testCase3} { t.Run(testCase.name, func(t *testing.T) { - _, err := NewKeycloakManager(testCase.inputConfig, &telemetry.MockAppMetrics{}) + _, err := NewKeycloakManager(OIDCConfig{}, testCase.inputConfig, &telemetry.MockAppMetrics{}) testCase.assertErrFunc(t, err, testCase.assertErrFuncMessage) }) } diff --git a/management/server/idp/zitadel.go b/management/server/idp/zitadel.go index b5ff2f914..35cd90700 100644 --- a/management/server/idp/zitadel.go +++ b/management/server/idp/zitadel.go @@ -30,9 +30,9 @@ type ZitadelManager struct { type ZitadelClientConfig struct { ClientID string ClientSecret string - GrantType string - TokenEndpoint string - ManagementEndpoint string + GrantType string `json:"-"` + TokenEndpoint string `json:"-"` + ManagementEndpoint string `json:"-"` } // ZitadelCredentials zitadel authentication information. @@ -85,7 +85,8 @@ type zitadelProfile struct { } // NewZitadelManager creates a new instance of the ZitadelManager. -func NewZitadelManager(config ZitadelClientConfig, appMetrics telemetry.AppMetrics) (*ZitadelManager, error) { +func NewZitadelManager(oidcConfig OIDCConfig, config ZitadelClientConfig, + appMetrics telemetry.AppMetrics) (*ZitadelManager, error) { httpTransport := http.DefaultTransport.(*http.Transport).Clone() httpTransport.MaxIdleConns = 5 @@ -95,6 +96,9 @@ func NewZitadelManager(config ZitadelClientConfig, appMetrics telemetry.AppMetri } helper := JsonParser{} + config.TokenEndpoint = oidcConfig.TokenEndpoint + config.ManagementEndpoint = fmt.Sprintf("%s/management/v1", oidcConfig.Issuer) + config.GrantType = "client_credentials" if config.ClientID == "" { return nil, fmt.Errorf("zitadel IdP configuration is incomplete, clientID is missing") @@ -104,18 +108,6 @@ func NewZitadelManager(config ZitadelClientConfig, appMetrics telemetry.AppMetri return nil, fmt.Errorf("zitadel IdP configuration is incomplete, ClientSecret is missing") } - if config.TokenEndpoint == "" { - return nil, fmt.Errorf("zitadel IdP configuration is incomplete, TokenEndpoint is missing") - } - - if config.ManagementEndpoint == "" { - return nil, fmt.Errorf("zitadel IdP configuration is incomplete, ManagementEndpoint is missing") - } - - if config.GrantType != "client_credentials" { - return nil, fmt.Errorf("zitadel idp configuration failed. Grant Type should be client_credentials") - } - credentials := &ZitadelCredentials{ clientConfig: config, httpClient: httpClient, diff --git a/management/server/idp/zitadel_test.go b/management/server/idp/zitadel_test.go index 9017883fe..736422ab8 100644 --- a/management/server/idp/zitadel_test.go +++ b/management/server/idp/zitadel_test.go @@ -45,19 +45,19 @@ func TestNewZitadelManager(t *testing.T) { assertErrFuncMessage: "should return error when field empty", } - testCase5Config := defaultTestConfig - testCase5Config.GrantType = "authorization_code" + testCase3Config := defaultTestConfig + testCase3Config.ClientSecret = "" - testCase5 := test{ - name: "Wrong GrantType", - inputConfig: testCase5Config, + testCase3 := test{ + name: "Missing ClientSecret Configuration", + inputConfig: testCase3Config, assertErrFunc: require.Error, - assertErrFuncMessage: "should return error when wrong grant type", + assertErrFuncMessage: "should return error when field empty", } - for _, testCase := range []test{testCase1, testCase2, testCase5} { + for _, testCase := range []test{testCase1, testCase2, testCase3} { t.Run(testCase.name, func(t *testing.T) { - _, err := NewZitadelManager(testCase.inputConfig, &telemetry.MockAppMetrics{}) + _, err := NewZitadelManager(OIDCConfig{}, testCase.inputConfig, &telemetry.MockAppMetrics{}) testCase.assertErrFunc(t, err, testCase.assertErrFuncMessage) }) }