diff --git a/frontend/public/locales/en-US/dashboard.json b/frontend/public/locales/en-US/dashboard.json
index 43356922..ef5473ab 100644
--- a/frontend/public/locales/en-US/dashboard.json
+++ b/frontend/public/locales/en-US/dashboard.json
@@ -840,6 +840,8 @@
"ssoIssuerDes": "Base URL of the OIDC issuer, e.g. <0>https://keycloak.example.com/realms/master0>. The provider metadata is fetched from <1>.well-known/openid-configuration1> under this URL.",
"ssoRegisterEnabled": "Allow automatic registration",
"ssoRegisterEnabledDes": "Automatically create a local account when a user signs in via SSO for the first time. The sign-up email filter below also applies.",
+ "ssoAutoRedirect": "Auto redirect to SSO",
+ "ssoAutoRedirectDes": "Skip the login form and send visitors straight to the identity provider. Append <0>?nosso=10> to the login URL to reach the password form (e.g. for admin recovery).",
"ssoCallbackUrl": "Callback URL",
"ssoCallbackUrlDes": "Register this URL as the redirect/callback URL in your identity provider: <0>{{url}}0>",
"themeVisible": "Visible",
diff --git a/frontend/public/locales/zh-CN/dashboard.json b/frontend/public/locales/zh-CN/dashboard.json
index 944d5352..755dfbcd 100644
--- a/frontend/public/locales/zh-CN/dashboard.json
+++ b/frontend/public/locales/zh-CN/dashboard.json
@@ -840,6 +840,8 @@
"ssoIssuerDes": "OIDC Issuer 的基础 URL,例如 <0>https://keycloak.example.com/realms/master0>。将从该地址下的 <1>.well-known/openid-configuration1> 获取提供方元数据。",
"ssoRegisterEnabled": "允许自动注册",
"ssoRegisterEnabledDes": "用户首次通过 SSO 登录时自动创建本地账号。下方的注册邮箱过滤规则同样适用。",
+ "ssoAutoRedirect": "自动跳转至 SSO",
+ "ssoAutoRedirectDes": "跳过登录表单,直接跳转至身份提供商。在登录地址后附加 <0>?nosso=10> 可进入密码登录表单(例如管理员账户恢复)。",
"ssoCallbackUrl": "回调地址",
"ssoCallbackUrlDes": "请在身份提供方中将此 URL 注册为 Redirect/Callback URL:<0>{{url}}0>",
"themeVisible": "可见",
diff --git a/frontend/src/api/site.ts b/frontend/src/api/site.ts
index 2103a7cd..67ca5773 100644
--- a/frontend/src/api/site.ts
+++ b/frontend/src/api/site.ts
@@ -30,6 +30,7 @@ export interface SiteConfig {
register_enabled?: boolean;
sso_enabled?: boolean;
sso_display_name?: string;
+ sso_auto_redirect?: boolean;
logo?: string;
logo_light?: string;
tos_url?: string;
diff --git a/frontend/src/component/Admin/Settings/Settings.tsx b/frontend/src/component/Admin/Settings/Settings.tsx
index c0b8a234..bee58c83 100644
--- a/frontend/src/component/Admin/Settings/Settings.tsx
+++ b/frontend/src/component/Admin/Settings/Settings.tsx
@@ -198,6 +198,7 @@ const Settings = () => {
"sso_client_secret",
"sso_scopes",
"sso_register_enabled",
+ "sso_auto_redirect",
"email_filter_mode",
"email_filter_list",
"email_disable_subaddress",
diff --git a/frontend/src/component/Admin/Settings/UserSession/SSOSettings.tsx b/frontend/src/component/Admin/Settings/UserSession/SSOSettings.tsx
index 057fb3a1..de41f106 100644
--- a/frontend/src/component/Admin/Settings/UserSession/SSOSettings.tsx
+++ b/frontend/src/component/Admin/Settings/UserSession/SSOSettings.tsx
@@ -141,6 +141,28 @@ const SSOSettings = () => {
/>
{t("settings.ssoRegisterEnabledDes")}
+
+
+ setSettings({
+ sso_auto_redirect: e.target.checked ? "1" : "0",
+ })
+ }
+ />
+ }
+ label={{t("settings.ssoAutoRedirect")}}
+ />
+
+ ]}
+ />
+
+
diff --git a/frontend/src/component/Pages/Login/Signin/SignIn.tsx b/frontend/src/component/Pages/Login/Signin/SignIn.tsx
index cfb05f55..078c6b64 100644
--- a/frontend/src/component/Pages/Login/Signin/SignIn.tsx
+++ b/frontend/src/component/Pages/Login/Signin/SignIn.tsx
@@ -14,7 +14,7 @@ import {
sendPrepareLogin,
sendResetEmail,
} from "../../../../api/api.ts";
-import { AppError, Code } from "../../../../api/request.ts";
+import { ApiPrefix, AppError, Code } from "../../../../api/request.ts";
import { AppRegistration, GrantService, LoginResponse, PrepareLoginResponse } from "../../../../api/user.ts";
import { clearOAuthApp, setOAuthApp, setOAuthAppLoading } from "../../../../redux/globalStateSlice.ts";
import { useAppDispatch, useAppSelector } from "../../../../redux/hooks.ts";
@@ -86,6 +86,7 @@ const EmailLogin = ({ oauthConsent }: SignInProps) => {
// Get OAuth app from Redux
const app = useAppSelector((state) => state.globalState.oauthApp);
+ const { sso_enabled, sso_auto_redirect } = useAppSelector((state) => state.siteConfig.login.config);
const [phase, setPhase] = useState(EmailLoginPhase.CollectEmail);
const [email, setEmail] = useState("");
@@ -312,6 +313,18 @@ const EmailLogin = ({ oauthConsent }: SignInProps) => {
});
}
+ // Auto-redirect to the configured SSO provider. `nosso` and an existing
+ // sso_error keep the password form reachable for admin recovery.
+ if (sso_enabled && sso_auto_redirect && !ssoError && !query.get("nosso") && !isOAuthFlow) {
+ const target = new URL(ApiPrefix + "/session/sso", window.location.origin);
+ const redirect = query.get("redirect");
+ if (redirect) {
+ target.searchParams.set("redirect", redirect);
+ }
+ window.location.replace(target.toString());
+ return;
+ }
+
const init = async () => {
if (isOAuthFlow) {
const registration = await loadAppRegistration();
diff --git a/inventory/setting.go b/inventory/setting.go
index 064a6d2d..fdc5e678 100644
--- a/inventory/setting.go
+++ b/inventory/setting.go
@@ -558,6 +558,7 @@ var DefaultSettings = map[string]string{
"sso_client_secret": "",
"sso_scopes": "",
"sso_register_enabled": "1",
+ "sso_auto_redirect": "0",
"email_filter_mode": "0",
"email_filter_list": "",
"email_disable_subaddress": "0",
diff --git a/pkg/setting/provider.go b/pkg/setting/provider.go
index ed6b3c46..3a169ce0 100644
--- a/pkg/setting/provider.go
+++ b/pkg/setting/provider.go
@@ -908,6 +908,7 @@ func (s *settingProvider) SSO(ctx context.Context) *SSO {
ClientSecret: s.getString(ctx, "sso_client_secret", ""),
Scopes: strings.Join(scopeList, " "),
RegisterEnabled: s.getBoolean(ctx, "sso_register_enabled", true),
+ AutoRedirect: s.getBoolean(ctx, "sso_auto_redirect", false),
}
}
diff --git a/pkg/setting/types.go b/pkg/setting/types.go
index d135f509..32fd7730 100644
--- a/pkg/setting/types.go
+++ b/pkg/setting/types.go
@@ -82,6 +82,7 @@ type SSO struct {
ClientSecret string
Scopes string
RegisterEnabled bool
+ AutoRedirect bool
}
type EmailFilterMode int
diff --git a/service/basic/site.go b/service/basic/site.go
index 3e1f5cb8..7e12babb 100644
--- a/service/basic/site.go
+++ b/service/basic/site.go
@@ -47,6 +47,7 @@ type SiteConfig struct {
PrivacyPolicyUrl string `json:"privacy_policy_url,omitempty"`
SSOEnabled bool `json:"sso_enabled,omitempty"`
SSODisplayName string `json:"sso_display_name,omitempty"`
+ SSOAutoRedirect bool `json:"sso_auto_redirect,omitempty"`
// Explorer section
Icons string `json:"icons,omitempty"`
@@ -106,6 +107,7 @@ func (s *GetSettingService) GetSiteConfig(c *gin.Context) (*SiteConfig, error) {
TosUrl: legalDocs.TermsOfService,
SSOEnabled: sso.Enabled && sso.Issuer != "" && sso.ClientID != "",
SSODisplayName: sso.DisplayName,
+ SSOAutoRedirect: sso.AutoRedirect,
}, nil
case "explorer":
explorerSettings := settings.ExplorerFrontendSettings(c)