diff --git a/combined/cmd/config.go b/combined/cmd/config.go index 37018cbed..20a155319 100644 --- a/combined/cmd/config.go +++ b/combined/cmd/config.go @@ -138,6 +138,7 @@ type AuthConfig struct { SignKeyRefreshEnabled bool `yaml:"signKeyRefreshEnabled"` MfaSessionMaxLifetime string `yaml:"mfaSessionMaxLifetime"` MfaSessionIdleTimeout string `yaml:"mfaSessionIdleTimeout"` + MfaSessionRememberMe bool `yaml:"mfaSessionRememberMe"` Storage AuthStorageConfig `yaml:"storage"` DashboardRedirectURIs []string `yaml:"dashboardRedirectURIs"` CLIRedirectURIs []string `yaml:"cliRedirectURIs"` @@ -590,6 +591,7 @@ func (c *CombinedConfig) buildEmbeddedIdPConfig(mgmt ManagementConfig) (*idp.Emb SignKeyRefreshEnabled: mgmt.Auth.SignKeyRefreshEnabled, MfaSessionMaxLifetime: mgmt.Auth.MfaSessionMaxLifetime, MfaSessionIdleTimeout: mgmt.Auth.MfaSessionIdleTimeout, + MfaSessionRememberMe: mgmt.Auth.MfaSessionRememberMe, Storage: idp.EmbeddedStorageConfig{ Type: authStorageType, Config: idp.EmbeddedStorageTypeConfig{ diff --git a/combined/config.yaml.example b/combined/config.yaml.example index 3c479bcf6..d0c4088cf 100644 --- a/combined/config.yaml.example +++ b/combined/config.yaml.example @@ -89,6 +89,7 @@ server: # MFA session settings (applies when TOTP is enabled for an account) # mfaSessionMaxLifetime: "24h" # Max duration for an MFA session from creation # mfaSessionIdleTimeout: "1h" # MFA session expires after this idle period + # mfaSessionRememberMe: false # Pre-check "remember me" on login so the MFA session persists across tabs/restarts # OAuth2 redirect URIs for dashboard dashboardRedirectURIs: - "https://app.example.com/nb-auth" diff --git a/management/server/idp/embedded.go b/management/server/idp/embedded.go index 7194bc363..8b4aa9a8b 100644 --- a/management/server/idp/embedded.go +++ b/management/server/idp/embedded.go @@ -56,6 +56,10 @@ type EmbeddedIdPConfig struct { // MfaSessionIdleTimeout is the idle timeout after which the MFA session expires (e.g. "1h"). // Defaults to "1h" if empty. MfaSessionIdleTimeout string + // MfaSessionRememberMe controls the default state of the "remember me" checkbox on the + // login screen. When true, the session cookie persists across browser tabs/restarts so + // MFA is not re-prompted until the session expires. Defaults to false. + MfaSessionRememberMe 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 @@ -184,7 +188,7 @@ func (c *EmbeddedIdPConfig) ToYAMLConfig() (*dex.YAMLConfig, error) { // Always initialize MFA providers and sessions so TOTP can be toggled at runtime. // MFAChain on clients is NOT set here — it's synced from the DB setting on startup. - if err := configureMFA(cfg, c.MfaSessionMaxLifetime, c.MfaSessionIdleTimeout); err != nil { + if err := configureMFA(cfg, c.MfaSessionMaxLifetime, c.MfaSessionIdleTimeout, c.MfaSessionRememberMe); err != nil { return nil, err } @@ -225,7 +229,7 @@ func sanitizePostLogoutRedirectURIs(uris []string) []string { return result } -func configureMFA(cfg *dex.YAMLConfig, sessionMaxLifetime, sessionIdleTimeout string) error { +func configureMFA(cfg *dex.YAMLConfig, sessionMaxLifetime, sessionIdleTimeout string, rememberMe bool) error { cfg.MFA.Authenticators = []dex.MFAAuthenticator{{ ID: "default-totp", // Has to be caps otherwise it will fail @@ -243,13 +247,11 @@ func configureMFA(cfg *dex.YAMLConfig, sessionMaxLifetime, sessionIdleTimeout st sessionIdleTimeout = "1h" } - rememberMeEnabled := false - cfg.Sessions = &dex.Sessions{ CookieName: "netbird-session", AbsoluteLifetime: sessionMaxLifetime, ValidIfNotUsedFor: sessionIdleTimeout, - RememberMeCheckedByDefault: &rememberMeEnabled, + RememberMeCheckedByDefault: &rememberMe, SSOSharedWithDefault: "", } // Absolutely required, otherwise the dex server will omit the MFA configuration entirely