From 089cd0d0b5e5375df88e7ef06df46986720bed29 Mon Sep 17 00:00:00 2001 From: mnianqi Date: Tue, 25 Aug 2026 16:58:14 +0800 Subject: [PATCH 1/3] feat: TAP-12033 validate saml config and redirect login page when enable sso --- apps/daas/src/i18n/langs/en.js | 5 ++ apps/daas/src/i18n/langs/zh-CN.js | 5 ++ apps/daas/src/i18n/langs/zh-TW.js | 5 ++ apps/daas/src/router/index.ts | 14 +++- apps/daas/src/views/login/Login.vue | 52 +++++++++++++- apps/daas/src/views/setting/Setting.vue | 94 +++++++++++++++++++++++++ packages/api/src/core/sso.ts | 6 ++ 7 files changed, 178 insertions(+), 3 deletions(-) diff --git a/apps/daas/src/i18n/langs/en.js b/apps/daas/src/i18n/langs/en.js index f2a1e4970..957d432ad 100644 --- a/apps/daas/src/i18n/langs/en.js +++ b/apps/daas/src/i18n/langs/en.js @@ -935,6 +935,11 @@ export default { setting_Saml_Login_Redirect_Url_doc: 'Post-login redirect URL after a successful SSO login', setting_saml_generate_keypair: 'Generate Key Pair', + setting_saml_test_config: 'Test Configuration', + setting_saml_static_check: 'Static Configuration Check', + setting_saml_test_success: 'SAML configuration test passed', + setting_saml_test_failed: 'SAML configuration test failed. See the details below.', + setting_saml_test_no_result: 'No SAML test result was returned. Please try again.', setting_saml_export_sp_metadata: 'Export SP Metadata', setting_saml_import_idp_metadata: 'Import IdP Metadata', setting_saml_import_idp_metadata_label: 'Upload the IdP metadata XML file', diff --git a/apps/daas/src/i18n/langs/zh-CN.js b/apps/daas/src/i18n/langs/zh-CN.js index 1ea267d4c..2804c196a 100644 --- a/apps/daas/src/i18n/langs/zh-CN.js +++ b/apps/daas/src/i18n/langs/zh-CN.js @@ -878,6 +878,11 @@ export default { '即时(JIT)预配:首次 SSO 登录时自动创建用户(默认关闭)', setting_Saml_Login_Redirect_Url_doc: 'SSO 登录成功后的跳转地址', setting_saml_generate_keypair: '生成密钥对', + setting_saml_test_config: '测试配置', + setting_saml_static_check: '配置静态检查', + setting_saml_test_success: 'SAML 配置测试通过', + setting_saml_test_failed: 'SAML 配置测试失败,请查看详细提示', + setting_saml_test_no_result: '未获取到 SAML 测试结果,请稍后重试', setting_saml_export_sp_metadata: '导出 SP 元数据', setting_saml_import_idp_metadata: '导入 IdP 元数据', setting_saml_import_idp_metadata_label: '上传 IdP 元数据 XML 文件', diff --git a/apps/daas/src/i18n/langs/zh-TW.js b/apps/daas/src/i18n/langs/zh-TW.js index b62361be2..d8f3c8b06 100644 --- a/apps/daas/src/i18n/langs/zh-TW.js +++ b/apps/daas/src/i18n/langs/zh-TW.js @@ -874,6 +874,11 @@ export default { '即時(JIT)佈建:首次 SSO 登入時自動創建用戶(預設關閉)', setting_Saml_Login_Redirect_Url_doc: 'SSO 登入成功後的跳轉地址', setting_saml_generate_keypair: '產生金鑰對', + setting_saml_test_config: '測試設定', + setting_saml_static_check: '設定靜態檢查', + setting_saml_test_success: 'SAML 設定測試通過', + setting_saml_test_failed: 'SAML 設定測試失敗,請查看詳細提示', + setting_saml_test_no_result: '未取得 SAML 測試結果,請稍後重試', setting_saml_export_sp_metadata: '匯出 SP 中繼資料', setting_saml_import_idp_metadata: '匯入 IdP 中繼資料', setting_saml_import_idp_metadata_label: '上傳 IdP 中繼資料 XML 檔案', diff --git a/apps/daas/src/router/index.ts b/apps/daas/src/router/index.ts index e6677ff5d..8f232eefe 100644 --- a/apps/daas/src/router/index.ts +++ b/apps/daas/src/router/index.ts @@ -71,9 +71,21 @@ router.beforeEach(async (to, from, next) => { 'registyResult', ].includes(to.name as string) ) { - next() + if (to.name === 'login' && to.query?.sso === '1') { + sessionStorage.setItem('samlManualLogin', '1') + next({ name: 'login' }) + } else { + next() + } } else { sessionStorage.setItem('lastLocationHref', location.href) + const ssoFromRoute = to.query?.sso === '1' + const ssoFromUrl = new URLSearchParams(window.location.search).get('sso') === '1' + if (ssoFromRoute || ssoFromUrl) { + // Keep the control flag out of the visible login URL. The login page + // consumes this one-time flag and stays available for manual SSO. + sessionStorage.setItem('samlManualLogin', '1') + } next('/login') } }) diff --git a/apps/daas/src/views/login/Login.vue b/apps/daas/src/views/login/Login.vue index 3fc1640bb..f40413211 100644 --- a/apps/daas/src/views/login/Login.vue +++ b/apps/daas/src/views/login/Login.vue @@ -33,12 +33,13 @@ export default { errorMessage: '', adEnable: false, samlEnable: false, + samlChecking: true, + samlRedirecting: false, } }, created() { useDark() this.loadAdEnable() - this.loadSamlEnable() if (this.$route.query) { this.form.email = this.$route.query.email const ssoError = this.$route.query.sso_error @@ -47,6 +48,12 @@ export default { } } }, + mounted() { + // Wait until the login route has finished mounting before starting the + // browser redirect. This keeps the automatic flow aligned with the + // existing button flow and avoids navigating during router resolution. + this.loadSamlEnable() + }, methods: { getSsoErrorMessage(code) { const messages = { @@ -64,9 +71,37 @@ export default { async loadSamlEnable() { try { this.samlEnable = await checkSamlLoginEnable() + if (this.samlEnable && this.shouldRedirectToSaml()) { + this.samlRedirecting = true + this.loginWithSaml() + return + } } catch { this.samlEnable = false + } finally { + if (!this.samlRedirecting) { + this.samlChecking = false + } + } + }, + shouldRedirectToSaml() { + const routeQuery = this.$route.query || {} + const ssoError = routeQuery.sso_error + + // sso=1 is used to bring an admin to the login page. It must not start + // an IdP redirect automatically; the SSO button remains available there. + if (sessionStorage.getItem('samlManualLogin') === '1') { + sessionStorage.removeItem('samlManualLogin') + return false } + + // Keep the login page visible after an SSO failure so the user can read + // the error and use the original password login flow if needed. + if (ssoError) { + return false + } + + return true }, loginWithSaml() { // Full browser navigation so the IdP redirect chain runs in the tab. @@ -160,7 +195,15 @@ export default {