diff --git a/.changeset/adr-0024-sso-nav.md b/.changeset/adr-0024-sso-nav.md new file mode 100644 index 0000000000..717fc91ddd --- /dev/null +++ b/.changeset/adr-0024-sso-nav.md @@ -0,0 +1,7 @@ +--- +'@objectstack/plugin-auth': patch +--- + +Auth: surface "SSO Providers" in the Setup app nav when SSO is enabled (ADR-0024 / cloud#551) + +The `sys_sso_provider` admin object (register / list / delete external OIDC IdPs) had no navigation entry, so an admin could only reach it by direct URL. `AuthPlugin` now contributes an **"SSO Providers"** entry into the Setup app's **Access Control** group — but only when the external-IdP RP is wired (`AuthManager.isSsoWired()`, which captures both self-host `OS_SSO_ENABLED` and the cloud per-env `planAllowsSso` arriving via `plugins.sso`). Owning-plugin-contributes pattern (ADR-0029 K2), mirroring `plugin-security`. `isSsoWired()` is made public for this gate. diff --git a/packages/plugins/plugin-auth/src/auth-manager.ts b/packages/plugins/plugin-auth/src/auth-manager.ts index 17c10c7d56..9a35abe26f 100644 --- a/packages/plugins/plugin-auth/src/auth-manager.ts +++ b/packages/plugins/plugin-auth/src/auth-manager.ts @@ -1970,8 +1970,11 @@ export class AuthManager { * in `buildPlugins()` (`ssoFromEnv ?? pluginConfig.sso ?? false`) so the * advertised capability can never disagree with the actual `/sign-in/sso` * route. `OS_SSO_ENABLED` (when set) wins over the config-file setting. + * Public so `AuthPlugin` can gate the Setup-nav "SSO Providers" entry on it + * (captures both self-host `OS_SSO_ENABLED` and the cloud per-env + * `planAllowsSso` config, since that arrives via `plugins.sso`). */ - private isSsoWired(): boolean { + public isSsoWired(): boolean { const ssoEnv = (globalThis as any)?.process?.env?.OS_SSO_ENABLED; const ssoFromEnv = ssoEnv != null ? String(ssoEnv).toLowerCase() === 'true' : undefined; return ssoFromEnv ?? (this.config.plugins as any)?.sso ?? false; diff --git a/packages/plugins/plugin-auth/src/auth-plugin.ts b/packages/plugins/plugin-auth/src/auth-plugin.ts index b2d8cfcef2..9a03c9f7df 100644 --- a/packages/plugins/plugin-auth/src/auth-plugin.ts +++ b/packages/plugins/plugin-auth/src/auth-plugin.ts @@ -200,6 +200,34 @@ export class AuthPlugin implements Plugin { dashboards: [SystemOverviewDashboard], // ADR-0021 — datasets backing the System Overview dashboard's widgets. datasets: SystemOverviewDatasets, + // ADR-0024 / cloud#551 — surface "SSO Providers" (sys_sso_provider) in the + // Setup app's Access Control group, but ONLY when the external-IdP RP is + // wired (self-host `OS_SSO_ENABLED`, or the cloud per-env `planAllowsSso` + // arriving via `plugins.sso`). Without the gate the entry would render an + // empty list + a "Register" button whose endpoint 404s when SSO is off. + // Owning-plugin-contributes pattern (ADR-0029 K2), mirroring plugin-security. + ...(this.authManager.isSsoWired() + ? { + navigationContributions: [ + { + app: 'setup', + group: 'group_access_control', + // After Roles/Permission-Sets (100) and Sharing (200), near API Keys (300). + priority: 250, + items: [ + { + id: 'nav_sso_providers', + type: 'object', + label: 'SSO Providers', + objectName: 'sys_sso_provider', + icon: 'log-in', + requiredPermissions: ['manage_platform_settings'], + }, + ], + }, + ], + } + : {}), }); ctx.logger.info('Auth Plugin initialized successfully');