diff --git a/changes/31480-fix-sso-alternate-url b/changes/31480-fix-sso-alternate-url
new file mode 100644
index 0000000000..b338ca764f
--- /dev/null
+++ b/changes/31480-fix-sso-alternate-url
@@ -0,0 +1 @@
+Added `sso_server_url` configuration option to support SSO setups with separate URLs for admin access vs agent/API access. When set, SSO authentication will only work from the specified URL. This fixes SSO authentication errors for organizations using dual URL configurations.
\ No newline at end of file
diff --git a/cmd/fleetctl/fleetctl/generate_gitops.go b/cmd/fleetctl/fleetctl/generate_gitops.go
index 9967b4dd65..d4161c6f84 100644
--- a/cmd/fleetctl/fleetctl/generate_gitops.go
+++ b/cmd/fleetctl/fleetctl/generate_gitops.go
@@ -647,6 +647,7 @@ func (cmd *GenerateGitopsCommand) generateSSOSettings(ssoSettings *fleet.SSOSett
jsonFieldName(t, "Metadata"): ssoSettings.Metadata,
jsonFieldName(t, "MetadataURL"): ssoSettings.MetadataURL,
jsonFieldName(t, "EnableSSOIdPLogin"): ssoSettings.EnableSSOIdPLogin,
+ jsonFieldName(t, "SSOServerURL"): ssoSettings.SSOServerURL,
}
if cmd.AppConfig.License.IsPremium() {
result[jsonFieldName(t, "EnableJITProvisioning")] = ssoSettings.EnableJITProvisioning
diff --git a/cmd/fleetctl/fleetctl/gitops_test.go b/cmd/fleetctl/fleetctl/gitops_test.go
index d2fd32dc47..aba6e3977e 100644
--- a/cmd/fleetctl/fleetctl/gitops_test.go
+++ b/cmd/fleetctl/fleetctl/gitops_test.go
@@ -2955,6 +2955,55 @@ func TestGitOpsSSOSettings(t *testing.T) {
require.Nil(t, appConfig.SSOSettings)
}
+func TestGitOpsSSOServerURL(t *testing.T) {
+ tmpDir := t.TempDir()
+ globalFile, err := os.CreateTemp(tmpDir, "*.yml")
+ require.NoError(t, err)
+ _, err = globalFile.WriteString(`
+controls:
+queries:
+policies:
+agent_options:
+org_settings:
+ server_settings:
+ server_url: ` + fleetServerURL + `
+ org_info:
+ org_name: ` + orgName + `
+ sso_settings:
+ entity_id: "test-entity"
+ idp_name: "Test IdP"
+ metadata: "test-metadata"
+ enable_sso: true
+ sso_server_url: "https://sso.example.com"
+ secrets:
+ - secret: test-secret
+`)
+ require.NoError(t, err)
+ globalFile.Close()
+
+ ds, _, _ := testing_utils.SetupFullGitOpsPremiumServer(t)
+
+ appConfig := fleet.AppConfig{}
+
+ ds.AppConfigFunc = func(ctx context.Context) (*fleet.AppConfig, error) {
+ return &appConfig, nil
+ }
+
+ ds.SaveAppConfigFunc = func(ctx context.Context, config *fleet.AppConfig) error {
+ appConfig = *config
+ return nil
+ }
+
+ // Run GitOps with SSO settings including sso_url
+ _, err = RunAppNoChecks([]string{"gitops", "-f", globalFile.Name()})
+ require.NoError(t, err)
+
+ require.NotNil(t, appConfig.SSOSettings)
+ require.Equal(t, "https://sso.example.com", appConfig.SSOSettings.SSOServerURL)
+ require.Equal(t, "test-entity", appConfig.SSOSettings.EntityID)
+ require.True(t, appConfig.SSOSettings.EnableSSO)
+}
+
func TestGitOpsSMTPSettings(t *testing.T) {
globalFileBasic := createGlobalFileBasic(t, fleetServerURL, orgName)
ds, _, _ := testing_utils.SetupFullGitOpsPremiumServer(t)
diff --git a/cmd/fleetctl/fleetctl/testdata/expectedGetConfigAppConfigJson.json b/cmd/fleetctl/fleetctl/testdata/expectedGetConfigAppConfigJson.json
index 3f4fd1c581..d07ca79a04 100644
--- a/cmd/fleetctl/fleetctl/testdata/expectedGetConfigAppConfigJson.json
+++ b/cmd/fleetctl/fleetctl/testdata/expectedGetConfigAppConfigJson.json
@@ -55,7 +55,8 @@
"enable_jit_provisioning": false,
"enable_jit_role_sync": false,
"enable_sso": false,
- "enable_sso_idp_login": false
+ "enable_sso_idp_login": false,
+ "sso_server_url": ""
},
"fleet_desktop": {
"transparency_url": "https://fleetdm.com/transparency"
diff --git a/cmd/fleetctl/fleetctl/testdata/expectedGetConfigAppConfigYaml.yml b/cmd/fleetctl/fleetctl/testdata/expectedGetConfigAppConfigYaml.yml
index face4d1ec8..9d3645832e 100644
--- a/cmd/fleetctl/fleetctl/testdata/expectedGetConfigAppConfigYaml.yml
+++ b/cmd/fleetctl/fleetctl/testdata/expectedGetConfigAppConfigYaml.yml
@@ -107,6 +107,7 @@ spec:
issuer_uri: ""
metadata: ""
metadata_url: ""
+ sso_server_url: ""
vulnerability_settings:
databases_path: /some/path
webhook_settings:
diff --git a/cmd/fleetctl/fleetctl/testdata/expectedGetConfigIncludeServerConfigJson.json b/cmd/fleetctl/fleetctl/testdata/expectedGetConfigIncludeServerConfigJson.json
index a605a10bc6..bda5cbfdaa 100644
--- a/cmd/fleetctl/fleetctl/testdata/expectedGetConfigIncludeServerConfigJson.json
+++ b/cmd/fleetctl/fleetctl/testdata/expectedGetConfigIncludeServerConfigJson.json
@@ -112,7 +112,8 @@
"metadata_url": "",
"idp_name": "",
"enable_sso": false,
- "enable_sso_idp_login": false
+ "enable_sso_idp_login": false,
+ "sso_server_url": ""
},
"fleet_desktop": {
"transparency_url": "https://fleetdm.com/transparency"
diff --git a/cmd/fleetctl/fleetctl/testdata/expectedGetConfigIncludeServerConfigYaml.yml b/cmd/fleetctl/fleetctl/testdata/expectedGetConfigIncludeServerConfigYaml.yml
index 19e6e9ba89..ceaf06c7ef 100644
--- a/cmd/fleetctl/fleetctl/testdata/expectedGetConfigIncludeServerConfigYaml.yml
+++ b/cmd/fleetctl/fleetctl/testdata/expectedGetConfigIncludeServerConfigYaml.yml
@@ -147,6 +147,7 @@ spec:
issuer_uri: ""
metadata: ""
metadata_url: ""
+ sso_server_url: ""
update_interval:
osquery_detail: 1h0m0s
osquery_policy: 1h0m0s
diff --git a/cmd/fleetctl/fleetctl/testdata/generateGitops/appConfig.json b/cmd/fleetctl/fleetctl/testdata/generateGitops/appConfig.json
index 78a492952f..549e288f6f 100644
--- a/cmd/fleetctl/fleetctl/testdata/generateGitops/appConfig.json
+++ b/cmd/fleetctl/fleetctl/testdata/generateGitops/appConfig.json
@@ -143,7 +143,8 @@
"enable_sso": true,
"enable_sso_idp_login": false,
"enable_jit_provisioning": true,
- "enable_jit_role_sync": false
+ "enable_jit_role_sync": false,
+ "sso_server_url": "https://sso.fleetdm.com"
},
"fleet_desktop": {
"transparency_url": "https://fleetdm.com/transparency"
diff --git a/cmd/fleetctl/fleetctl/testdata/generateGitops/expectedOrgSettings-insecure.yaml b/cmd/fleetctl/fleetctl/testdata/generateGitops/expectedOrgSettings-insecure.yaml
index 77a1bb961c..d517c31996 100644
--- a/cmd/fleetctl/fleetctl/testdata/generateGitops/expectedOrgSettings-insecure.yaml
+++ b/cmd/fleetctl/fleetctl/testdata/generateGitops/expectedOrgSettings-insecure.yaml
@@ -101,6 +101,7 @@ sso_settings:
idp_name: some-idp-name
metadata: some-sso-metadata
metadata_url: http://some-sso-metadata-url.com
+ sso_server_url: https://sso.fleetdm.com
webhook_settings:
activities_webhook:
destination_url: https://some-activities-webhook-url.com
diff --git a/cmd/fleetctl/fleetctl/testdata/generateGitops/expectedOrgSettings.yaml b/cmd/fleetctl/fleetctl/testdata/generateGitops/expectedOrgSettings.yaml
index 70f5694cb0..b793bbeae3 100644
--- a/cmd/fleetctl/fleetctl/testdata/generateGitops/expectedOrgSettings.yaml
+++ b/cmd/fleetctl/fleetctl/testdata/generateGitops/expectedOrgSettings.yaml
@@ -101,6 +101,7 @@ sso_settings:
idp_name: some-idp-name
metadata: ___GITOPS_COMMENT_9___
metadata_url: ___GITOPS_COMMENT_10___
+ sso_server_url: https://sso.fleetdm.com
webhook_settings:
activities_webhook:
destination_url: https://some-activities-webhook-url.com
diff --git a/cmd/fleetctl/fleetctl/testdata/generateGitops/test_dir_free/default.yml b/cmd/fleetctl/fleetctl/testdata/generateGitops/test_dir_free/default.yml
index 375d3e942b..207e7f6489 100644
--- a/cmd/fleetctl/fleetctl/testdata/generateGitops/test_dir_free/default.yml
+++ b/cmd/fleetctl/fleetctl/testdata/generateGitops/test_dir_free/default.yml
@@ -136,6 +136,7 @@ org_settings:
idp_name: some-idp-name
metadata: # TODO: Add your SSO metadata here
metadata_url: # TODO: Add your SSO metadata URL here
+ sso_server_url: https://sso.fleetdm.com
webhook_settings:
activities_webhook:
destination_url: https://some-activities-webhook-url.com
diff --git a/cmd/fleetctl/fleetctl/testdata/generateGitops/test_dir_premium/default.yml b/cmd/fleetctl/fleetctl/testdata/generateGitops/test_dir_premium/default.yml
index 8f34f609e2..0f8e320421 100644
--- a/cmd/fleetctl/fleetctl/testdata/generateGitops/test_dir_premium/default.yml
+++ b/cmd/fleetctl/fleetctl/testdata/generateGitops/test_dir_premium/default.yml
@@ -135,6 +135,7 @@ org_settings:
idp_name: some-idp-name
metadata: # TODO: Add your SSO metadata here
metadata_url: # TODO: Add your SSO metadata URL here
+ sso_server_url: https://sso.fleetdm.com
webhook_settings:
activities_webhook:
destination_url: https://some-activities-webhook-url.com
diff --git a/cmd/fleetctl/fleetctl/testdata/macosSetupExpectedAppConfigEmpty.yml b/cmd/fleetctl/fleetctl/testdata/macosSetupExpectedAppConfigEmpty.yml
index 3d57b1adc9..38e0b2d04e 100644
--- a/cmd/fleetctl/fleetctl/testdata/macosSetupExpectedAppConfigEmpty.yml
+++ b/cmd/fleetctl/fleetctl/testdata/macosSetupExpectedAppConfigEmpty.yml
@@ -107,6 +107,7 @@ spec:
issuer_uri: ""
metadata: ""
metadata_url: ""
+ sso_server_url: ""
vulnerability_settings:
databases_path: ""
webhook_settings:
diff --git a/cmd/fleetctl/fleetctl/testdata/macosSetupExpectedAppConfigSet.yml b/cmd/fleetctl/fleetctl/testdata/macosSetupExpectedAppConfigSet.yml
index e65340a4fd..5b71b1b060 100644
--- a/cmd/fleetctl/fleetctl/testdata/macosSetupExpectedAppConfigSet.yml
+++ b/cmd/fleetctl/fleetctl/testdata/macosSetupExpectedAppConfigSet.yml
@@ -107,6 +107,7 @@ spec:
issuer_uri: ""
metadata: ""
metadata_url: ""
+ sso_server_url: ""
vulnerability_settings:
databases_path: ""
webhook_settings:
diff --git a/docs/Configuration/yaml-files.md b/docs/Configuration/yaml-files.md
index 3f6fc2b2ac..f70a0ac796 100644
--- a/docs/Configuration/yaml-files.md
+++ b/docs/Configuration/yaml-files.md
@@ -664,6 +664,7 @@ org_settings:
metadata: $SSO_METADATA
enable_jit_provisioning: true # Available in Fleet Premium
enable_sso_idp_login: true
+ sso_server_url: https://admin.example.com # Optional, SSO will only work from this URL
```
### integrations
diff --git a/docs/REST API/rest-api.md b/docs/REST API/rest-api.md
index a95d59442e..5a95bec445 100644
--- a/docs/REST API/rest-api.md
+++ b/docs/REST API/rest-api.md
@@ -802,7 +802,8 @@ None.
"idp_name": "",
"enable_sso": false,
"enable_sso_idp_login": false,
- "enable_jit_provisioning": false
+ "enable_jit_provisioning": false,
+ "sso_server_url": ""
},
"conditional_access": {
"microsoft_entra_tenant_id": "",
@@ -1443,7 +1444,8 @@ Modifies the Fleet's configuration with the supplied information.
"metadata": "",
"idp_name": "",
"enable_sso_idp_login": false,
- "enable_jit_provisioning": false
+ "enable_jit_provisioning": false,
+ "sso_server_url": ""
}
}
```
diff --git a/frontend/interfaces/config.ts b/frontend/interfaces/config.ts
index bef8b5e17e..a37af8c5b5 100644
--- a/frontend/interfaces/config.ts
+++ b/frontend/interfaces/config.ts
@@ -147,6 +147,7 @@ export interface IConfig {
enable_sso_idp_login: boolean;
enable_jit_provisioning: boolean;
enable_jit_role_sync: boolean;
+ sso_server_url?: string;
};
// configuration details for conditional access. For enabled/disabled status per team, see
// subfields under `integrations`
diff --git a/frontend/pages/admin/OrgSettingsPage/cards/Advanced/Advanced.tsx b/frontend/pages/admin/OrgSettingsPage/cards/Advanced/Advanced.tsx
index 699e16ffb6..1d74309d3a 100644
--- a/frontend/pages/admin/OrgSettingsPage/cards/Advanced/Advanced.tsx
+++ b/frontend/pages/admin/OrgSettingsPage/cards/Advanced/Advanced.tsx
@@ -18,6 +18,7 @@ import { IAppConfigFormProps, IFormField } from "../constants";
const baseClass = "app-config-form";
interface IAdvancedConfigFormData {
+ ssoUserURL: string;
mdmAppleServerURL: string;
domain: string;
verifySSLCerts: boolean;
@@ -33,12 +34,14 @@ interface IAdvancedConfigFormData {
}
interface IAdvancedConfigFormErrors {
+ ssoUserURL?: string | null;
mdmAppleServerURL?: string | null;
domain?: string | null;
hostExpiryWindow?: string | null;
}
const validateFormData = ({
+ ssoUserURL,
mdmAppleServerURL,
domain,
hostExpiryWindow,
@@ -46,6 +49,12 @@ const validateFormData = ({
}: IAdvancedConfigFormData) => {
const errors: Record = {};
+ if (!ssoUserURL) {
+ delete errors.ssoUserURL;
+ } else if (!validUrl({ url: ssoUserURL })) {
+ errors.ssoUserURL = `${ssoUserURL} is not a valid URL`;
+ }
+
if (!mdmAppleServerURL) {
delete errors.mdmAppleServerURL;
} else if (!validUrl({ url: mdmAppleServerURL })) {
@@ -75,6 +84,7 @@ const Advanced = ({
const gitOpsModeEnabled = appConfig.gitops.gitops_mode_enabled;
const [formData, setFormData] = useState({
+ ssoUserURL: appConfig.sso_settings?.sso_server_url || "",
mdmAppleServerURL: appConfig.mdm?.apple_server_url || "",
domain: appConfig.smtp_settings?.domain || "",
verifySSLCerts: appConfig.smtp_settings?.verify_ssl_certs || false,
@@ -97,6 +107,7 @@ const Advanced = ({
});
const {
+ ssoUserURL,
mdmAppleServerURL,
domain,
verifySSLCerts,
@@ -179,6 +190,9 @@ const Advanced = ({
mdm: {
apple_server_url: mdmAppleServerURL,
},
+ sso_settings: {
+ sso_server_url: ssoUserURL,
+ },
};
handleSubmit(formDataToSubmit);
@@ -193,6 +207,25 @@ const Advanced = ({
Most users do not need to modify these options.
+ (
+
+ )}
+ />
{appConfig.mdm.enabled_and_configured && (
+
+
+
+
+ MIIDXTCCAkWgAwIBAgIJALmVVuDWu4NYMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMTYxMjMxMTQzNDQ3WhcNNDgwNjI1MTQzNDQ3WjBFMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzUCFozgNb1h1M0jzNRSCjhOBnR+uVbVpaWfXYIR+AhWDdEe5ryY+CgavOg8bfLybyzFdehlYdDRgkedEB/GjG8aJw06l0qF4jDOAw0kEygWCu2mcH7XOxRt+YAH3TVHa/Hu1W3WjzkobqqqLQ8gkKWWM27fOgAZ6GieaJBN6VBSMMcPey3HWLBmc+TYJmv1dbaO2jHhKh8pfKw0W12VM8P1PIO8gv4Phu/uuJYieBWKixBEyy0lHjyixYFCR12xdh4CA47q958ZRGnnDUGFVE1QhgRacJCOZ9bd5t9mr8KLaVBYTCJo5ERE8jymab5dPqe5qKfJsCZiqWglbjUo9twIDAQABo1AwTjAdBgNVHQ4EFgQUxpuwcs/CYQOyui+r1G+3KxBNhxkwHwYDVR0jBBgwFoAUxpuwcs/CYQOyui+r1G+3KxBNhxkwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAAiWUKs/2x/viNCKi3Y6blEuCtAGhzOOZ9EjrvJ8+COH3Rag3tVBWrcBZ3/uhhPq5gy9lqw4OkvEws99/5jFsX1FJ6MKBgqfuy7yh5s1YfM0ANHYczMmYpZeAcQf2CGAaVfwTTfSlzNLsF2lW/ly7yapFzlYSJLGoVE+OHEu8g5SlNACUEfkXw+5Eghh+KzlIN7R6Q7r2ixWNFBC/jWf7NKUfJyX8qIG5md1YUeT6GBW9Bm2/1/RiO24JTaYlfLdKK9TYb8sG5B+OLab2DImG99CJ25RkAcSobWNF5zD0O6lgOo3cEdB/ksCq3hmtlC/DlLZ/D8CJ+7VuZnS1rR2naQ==
+
+
+
+
+ urn:oasis:names:tc:SAML:2.0:nameid-format:transient
+
+
+`
+
+ // Configure SSO with a specific SSO server URL and inline metadata
+ acResp := appConfigResponse{}
+ s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(fmt.Sprintf(`{
+ "sso_settings": {
+ "enable_sso": true,
+ "entity_id": "https://localhost:8080",
+ "idp_name": "SimpleSAML",
+ "metadata": %q,
+ "enable_jit_provisioning": false,
+ "sso_server_url": "https://admin.localhost:8080"
+ }
+ }`, testMetadata)), http.StatusOK, &acResp)
+ require.NotNil(t, acResp)
+
+ // Verify the SSO server URL is set
+ require.NotNil(t, acResp.SSOSettings)
+ require.Equal(t, "https://admin.localhost:8080", acResp.SSOSettings.SSOServerURL)
+
+ // Initiate SSO
+ var resIni initiateSSOResponse
+ s.DoJSON("POST", "/api/v1/fleet/sso", map[string]string{}, http.StatusOK, &resIni)
+ require.NotEmpty(t, resIni.URL)
+
+ // Parse the auth request to verify it uses the SSO URL
+ parsed, err := url.Parse(resIni.URL)
+ require.NoError(t, err)
+ q := parsed.Query()
+ encoded := q.Get("SAMLRequest")
+ assert.NotEmpty(t, encoded)
+ authReq := inflate(t, encoded)
+
+ // Check that the ACS URL in the auth request uses the SSO server URL
+ require.NotNil(t, authReq.AssertionConsumerServiceURL)
+ assert.Equal(t, "https://admin.localhost:8080/api/v1/fleet/sso/callback", authReq.AssertionConsumerServiceURL)
+}
diff --git a/server/service/sessions.go b/server/service/sessions.go
index c179e8e6ee..9a4ca571d5 100644
--- a/server/service/sessions.go
+++ b/server/service/sessions.go
@@ -441,7 +441,12 @@ func (svc *Service) InitiateSSO(ctx context.Context, redirectURL string) (sessio
}
serverURL := appConfig.ServerSettings.ServerURL
- acsURL := serverURL + svc.config.Server.URLPrefix + "/api/v1/fleet/sso/callback"
+ // Use SSO server URL if configured, otherwise use the server URL
+ ssoURL := serverURL
+ if appConfig.SSOSettings != nil && appConfig.SSOSettings.SSOServerURL != "" {
+ ssoURL = appConfig.SSOSettings.SSOServerURL
+ }
+ acsURL := ssoURL + svc.config.Server.URLPrefix + "/api/v1/fleet/sso/callback"
// If entityID is not explicitly set, default to host name.
//
@@ -650,7 +655,12 @@ func (svc *Service) InitSSOCallback(
}
serverURL := appConfig.ServerSettings.ServerURL
- acsURL, err := url.Parse(serverURL + svc.config.Server.URLPrefix + "/api/v1/fleet/sso/callback")
+ // Use SSO server URL if configured, otherwise use the server URL
+ ssoURL := serverURL
+ if appConfig.SSOSettings != nil && appConfig.SSOSettings.SSOServerURL != "" {
+ ssoURL = appConfig.SSOSettings.SSOServerURL
+ }
+ acsURL, err := url.Parse(ssoURL + svc.config.Server.URLPrefix + "/api/v1/fleet/sso/callback")
if err != nil {
return nil, "", ctxerr.Wrap(ctx, err, "failed to parse ACS URL")
}
@@ -658,7 +668,14 @@ func (svc *Service) InitSSOCallback(
expectedAudiences := []string{
appConfig.SSOSettings.EntityID,
appConfig.ServerSettings.ServerURL,
- appConfig.ServerSettings.ServerURL + svc.config.Server.URLPrefix + "/api/v1/fleet/sso/callback", // ACS
+ appConfig.ServerSettings.ServerURL + svc.config.Server.URLPrefix + "/api/v1/fleet/sso/callback", // ACS with server URL
+ }
+ // Add SSO server URL to expected audiences if configured
+ if appConfig.SSOSettings != nil && appConfig.SSOSettings.SSOServerURL != "" {
+ expectedAudiences = append(expectedAudiences,
+ appConfig.SSOSettings.SSOServerURL,
+ appConfig.SSOSettings.SSOServerURL+svc.config.Server.URLPrefix+"/api/v1/fleet/sso/callback", // ACS with SSO server URL
+ )
}
samlProvider, requestID, redirectURL, err := sso.SAMLProviderFromSessionOrConfiguredMetadata(
ctx, sessionID, svc.ssoSessionStore, acsURL, appConfig.SSOSettings, expectedAudiences,
diff --git a/server/service/sessions_test.go b/server/service/sessions_test.go
index 4d15af5aea..f8de66e61c 100644
--- a/server/service/sessions_test.go
+++ b/server/service/sessions_test.go
@@ -8,6 +8,7 @@ import (
"github.com/fleetdm/fleet/v4/server/config"
"github.com/fleetdm/fleet/v4/server/contexts/viewer"
"github.com/fleetdm/fleet/v4/server/datastore/mysql"
+ "github.com/fleetdm/fleet/v4/server/datastore/redis/redistest"
"github.com/fleetdm/fleet/v4/server/fleet"
"github.com/fleetdm/fleet/v4/server/mock"
"github.com/fleetdm/fleet/v4/server/ptr"
@@ -436,3 +437,54 @@ func TestGetSSOUser(t *testing.T) {
_, err = svc.GetSSOUser(ctx, auth)
require.Error(t, err)
}
+
+func TestInitiateSSOWithSSOServerURL(t *testing.T) {
+ ds := new(mock.Store)
+ pool := redistest.SetupRedis(t, t.Name(), false, false, false)
+
+ svc, ctx := newTestServiceWithConfig(t, ds, config.TestConfig(), nil, nil, &TestServerOpts{
+ Pool: pool,
+ })
+
+ // Mock app config with SSO server URL
+ appConfig := &fleet.AppConfig{
+ ServerSettings: fleet.ServerSettings{
+ ServerURL: "https://fleet.example.com",
+ },
+ SSOSettings: &fleet.SSOSettings{
+ EnableSSO: true,
+ SSOServerURL: "https://admin.fleet.example.com",
+ SSOProviderSettings: fleet.SSOProviderSettings{
+ EntityID: "fleet",
+ IDPName: "TestIDP",
+ Metadata: `
+
+
+
+
+
+ MIIDXTCCAkWgAwIBAgIJALmVVuDWu4NYMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMTYxMjMxMTQzNDQ3WhcNNDgwNjI1MTQzNDQ3WjBFMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzUCFozgNb1h1M0jzNRSCjhOBnR+uVbVpaWfXYIR+AhWDdEe5ryY+CgavOg8bfLybyzFdehlYdDRgkedEB/GjG8aJw06l0qF4jDOAw0kEygWCu2mcH7XOxRt+YAH3TVHa/Hu1W3WjzkobqqqLQ8gkKWWM27fOgAZ6GieaJBN6VBSMMcPey3HWLBmc+TYJmv1dbaO2jHhKh8pfKw0W12VM8P1PIO8gv4Phu/uuJYieBWKixBEyy0lHjyixYFCR12xdh4CA47q958ZRGnnDUGFVE1QhgRacJCOZ9bd5t9mr8KLaVBYTCJo5ERE8jymab5dPqe5qKfJsCZiqWglbjUo9twIDAQABo1AwTjAdBgNVHQ4EFgQUxpuwcs/CYQOyui+r1G+3KxBNhxkwHwYDVR0jBBgwFoAUxpuwcs/CYQOyui+r1G+3KxBNhxkwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAAiWUKs/2x/viNCKi3Y6blEuCtAGhzOOZ9EjrvJ8+COH3Rag3tVBWrcBZ3/uhhPq5gy9lqw4OkvEws99/5jFsX1FJ6MKBgqfuy7yh5s1YfM0ANHYczMmYpZeAcQf2CGAaVfwTTfSlzNLsF2lW/ly7yapFzlYSJLGoVE+OHEu8g5SlNACUEfkXw+5Eghh+KzlIN7R6Q7r2ixWNFBC/jWf7NKUfJyX8qIG5md1YUeT6GBW9Bm2/1/RiO24JTaYlfLdKK9TYb8sG5B+OLab2DImG99CJ25RkAcSobWNF5zD0O6lgOo3cEdB/ksCq3hmtlC/DlLZ/D8CJ+7VuZnS1rR2naQ==
+
+
+
+
+
+`,
+ },
+ },
+ }
+
+ ds.AppConfigFunc = func(ctx context.Context) (*fleet.AppConfig, error) {
+ return appConfig, nil
+ }
+
+ // Test that ACS URL uses SSO URL
+ sessionID, _, idpURL, err := svc.InitiateSSO(ctx, "/dashboard")
+ require.NoError(t, err)
+ require.NotEmpty(t, sessionID)
+ require.NotEmpty(t, idpURL)
+
+ // The ACS URL should use the SSO server URL
+ // We can't directly test the ACS URL in the SAML request here since it's embedded in the XML,
+ // but the integration test verifies this works correctly
+}
diff --git a/tools/cloner-check/generated_files/appconfig.txt b/tools/cloner-check/generated_files/appconfig.txt
index 5c15421c1f..9b91c90421 100644
--- a/tools/cloner-check/generated_files/appconfig.txt
+++ b/tools/cloner-check/generated_files/appconfig.txt
@@ -53,6 +53,7 @@ github.com/fleetdm/fleet/v4/server/fleet/SSOSettings EnableSSO bool
github.com/fleetdm/fleet/v4/server/fleet/SSOSettings EnableSSOIdPLogin bool
github.com/fleetdm/fleet/v4/server/fleet/SSOSettings EnableJITProvisioning bool
github.com/fleetdm/fleet/v4/server/fleet/SSOSettings EnableJITRoleSync bool
+github.com/fleetdm/fleet/v4/server/fleet/SSOSettings SSOServerURL string
github.com/fleetdm/fleet/v4/server/fleet/AppConfig FleetDesktop fleet.FleetDesktopSettings
github.com/fleetdm/fleet/v4/server/fleet/FleetDesktopSettings TransparencyURL string
github.com/fleetdm/fleet/v4/server/fleet/AppConfig VulnerabilitySettings fleet.VulnerabilitySettings