From b54e46cd485e49d747ce63d0ef2ab945f884bf12 Mon Sep 17 00:00:00 2001 From: jnfrati Date: Wed, 15 Apr 2026 17:04:45 +0200 Subject: [PATCH] add dashboard post logout redirect uri to embedded config --- combined/cmd/config.go | 22 ++++++++------- management/server/idp/embedded.go | 47 ++++++++++++++++++++++--------- 2 files changed, 46 insertions(+), 23 deletions(-) diff --git a/combined/cmd/config.go b/combined/cmd/config.go index 87e419707..36bb2fe95 100644 --- a/combined/cmd/config.go +++ b/combined/cmd/config.go @@ -133,14 +133,15 @@ type ManagementConfig struct { // AuthConfig contains authentication/identity provider settings type AuthConfig struct { - Issuer string `yaml:"issuer"` - LocalAuthDisabled bool `yaml:"localAuthDisabled"` - EnableLocalMFA bool `yaml:"enableLocalMFA"` - SignKeyRefreshEnabled bool `yaml:"signKeyRefreshEnabled"` - Storage AuthStorageConfig `yaml:"storage"` - DashboardRedirectURIs []string `yaml:"dashboardRedirectURIs"` - CLIRedirectURIs []string `yaml:"cliRedirectURIs"` - Owner *AuthOwnerConfig `yaml:"owner,omitempty"` + Issuer string `yaml:"issuer"` + LocalAuthDisabled bool `yaml:"localAuthDisabled"` + EnableLocalMFA bool `yaml:"enableLocalMFA"` + SignKeyRefreshEnabled bool `yaml:"signKeyRefreshEnabled"` + Storage AuthStorageConfig `yaml:"storage"` + DashboardRedirectURIs []string `yaml:"dashboardRedirectURIs"` + CLIRedirectURIs []string `yaml:"cliRedirectURIs"` + Owner *AuthOwnerConfig `yaml:"owner,omitempty"` + DashboardPostLogoutRedirectURIs []string `yaml:"dashboardPostLogoutRedirectURIs"` } // AuthStorageConfig contains auth storage settings @@ -592,8 +593,9 @@ func (c *CombinedConfig) buildEmbeddedIdPConfig(mgmt ManagementConfig) (*idp.Emb DSN: authStorageDSN, }, }, - DashboardRedirectURIs: mgmt.Auth.DashboardRedirectURIs, - CLIRedirectURIs: mgmt.Auth.CLIRedirectURIs, + DashboardRedirectURIs: mgmt.Auth.DashboardRedirectURIs, + CLIRedirectURIs: mgmt.Auth.CLIRedirectURIs, + DashboardPostLogoutRedirectURIs: mgmt.Auth.DashboardPostLogoutRedirectURIs, } if mgmt.Auth.Owner != nil && mgmt.Auth.Owner.Email != "" { diff --git a/management/server/idp/embedded.go b/management/server/idp/embedded.go index 3d6f5c7ca..45526f445 100644 --- a/management/server/idp/embedded.go +++ b/management/server/idp/embedded.go @@ -53,6 +53,12 @@ type EmbeddedIdPConfig struct { LocalAuthDisabled bool // EnableMFA will enforce TOTP multi factor authentication for local users EnableMFA bool + // Dashboard Post logout redirect URIs, these are required to tell + // Dex what to allow when an RP-Initiated logout is started by the frontend + // at least one of these must match the dashboard base URL or the dashboard + // DASHBOARD_POST_LOGOUT_URL environment variable + // WARNING: Dex only uses exact match, not wildcards + DashboardPostLogoutRedirectURIs []string // StaticConnectors are additional connectors to seed during initialization StaticConnectors []dex.Connector } @@ -127,10 +133,14 @@ func (c *EmbeddedIdPConfig) ToYAMLConfig() (*dex.YAMLConfig, error) { // Build dashboard redirect URIs including the OAuth callback for proxy authentication dashboardRedirectURIs := c.DashboardRedirectURIs baseURL := strings.TrimSuffix(c.Issuer, "/oauth2") - logoutURL := strings.TrimRight(baseURL, "/") + "/" // todo: resolve import cycle dashboardRedirectURIs = append(dashboardRedirectURIs, baseURL+"/api/reverse-proxy/callback") + dashboardPostLogoutRedirectURIs := c.DashboardPostLogoutRedirectURIs + // It is safe to assume that most installations will share the location of the + // MGMT api and the dashboard, adding baseURL means less configuration for the instance admin + dashboardPostLogoutRedirectURIs = append(dashboardPostLogoutRedirectURIs, baseURL) + cfg := &dex.YAMLConfig{ Issuer: c.Issuer, Storage: dex.Storage{ @@ -153,24 +163,17 @@ func (c *EmbeddedIdPConfig) ToYAMLConfig() (*dex.YAMLConfig, error) { EnablePasswordDB: true, StaticClients: []storage.Client{ { - ID: staticClientDashboard, - Name: "NetBird Dashboard", - Public: true, - RedirectURIs: dashboardRedirectURIs, - PostLogoutRedirectURIs: []string{ - c.Issuer, - logoutURL, - }, + ID: staticClientDashboard, + Name: "NetBird Dashboard", + Public: true, + RedirectURIs: dashboardRedirectURIs, + PostLogoutRedirectURIs: sanitizePostLogoutRedirectURIs(dashboardPostLogoutRedirectURIs), }, { ID: staticClientCLI, Name: "NetBird CLI", Public: true, RedirectURIs: cliRedirectURIs, - PostLogoutRedirectURIs: []string{ - c.Issuer, - logoutURL, - }, }, }, StaticConnectors: c.StaticConnectors, @@ -201,6 +204,24 @@ func (c *EmbeddedIdPConfig) ToYAMLConfig() (*dex.YAMLConfig, error) { return cfg, nil } +// Due to how the frontend generates the logout, sometimes it appends a trailing slash +// and because Dex only allows exact matches, we need to make sure we always have both +// versions of each provided uri +func sanitizePostLogoutRedirectURIs(uris []string) []string { + result := make([]string, 0) + for _, uri := range uris { + if strings.HasSuffix(uri, "/") { + result = append(result, uri) + result = append(result, strings.TrimSuffix(uri, "/")) + } else { + result = append(result, uri) + result = append(result, uri+"/") + } + } + + return result +} + func configureMFA(cfg *dex.YAMLConfig) error { totpConfig := dex.TOTPConfig{ Issuer: "NetBird",