Skip to content

feat(login): support /login?sso=xxx to auto redirect to SSO provider - #2249

Open
710leo wants to merge 1 commit into
mainfrom
feat/login-sso-auto-redirect
Open

feat(login): support /login?sso=xxx to auto redirect to SSO provider#2249
710leo wants to merge 1 commit into
mainfrom
feat/login-sso-auto-redirect

Conversation

@710leo

@710leo 710leo commented Aug 11, 2026

Copy link
Copy Markdown
Member

背景

登录页目前只能手动点击「其他登录方式」里的 SSO 链接。有些场景(内部门户跳转、书签、告警通知里的链接)希望直接落到第三方登录,省掉这一次点击。

Pro 版虽然有 global_sso_way 的全局自动跳转,但它由后端配置决定跳哪个,前端无法按链接指定。

改动

登录页新增 sso URL 参数,例如 /login?sso=oidc,等价于自动点了对应的 SSO 链接。

  • 支持的取值:oidc / cas / oauth(兼容 oauth2)/ custom / dingtalk / feishu,大小写不敏感
  • 无法识别的值当作没传,正常展示登录表单
  • 跳转期间展示 loading 遮罩,避免登录表单闪现、用户误输入
  • 该 SSO 未配置登录地址或接口异常时,复位 loading 把登录表单还给用户,并复用已有的 sso_no_url 提示,不会卡在转圈
  • redirect 参数照常透传给 SSO 接口
  • ?admin 仍然是「用本地账号登录」的逃生口,/login?admin&sso=oidc 不跳转

实现为纯增量,复用已有的 getRedirectURL* service,未改动原有的六个 SSO 链接,未新增 i18n key。

注意:CAS 需要在跳转前写 CAS_state,这段逻辑现在在自动跳转和手动点击两处各有一份,后续调整 CAS 需要同时改。

配套 PR(Pro):URL 上显式指定 sso 时,useSsoWay 不再按 global_sso_way 跳转,避免两处抢着写 window.location.href

验证

用 Playwright 对社区版构建(plus: 走 PlusPlaceholder)做的端到端验证:

场景 结果
/login 正常渲染、无 JS 运行时错误 PASS
?sso=oidc 未配置时还原表单 + 提示 PASS
?sso=oidc 跳转前展示遮罩、不闪表单 PASS
?sso=oidc 已配置时自动跳转 PASS
?sso=cas 自动跳转且写入 CAS_state PASS
?sso=OAuth2 大小写与别名兼容 PASS
?sso=oidc&redirect=/targets 透传 redirect PASS
?sso=garbage 展示表单、不发跳转请求 PASS
?sso=oidc&admin 保留本地表单、不跳转 PASS
原有六个 SSO 链接点击回归(含 CAS_state、未配置提示) PASS

Summary by CodeRabbit

  • New Features
    • Added automatic single sign-on redirection based on the selected provider.
    • Added support for OAuth2 and other configured SSO providers.
    • Added a loading indicator while redirecting to SSO.
    • Preserved the local login form when redirection is unavailable or fails.
    • Added support for forcing local administrator login.

Allow a link to jump straight to the configured third-party login
instead of requiring the user to click the SSO link on the login page.

- accepted values: oidc / cas / oauth (oauth2) / custom / dingtalk /
  feishu, case-insensitive; unknown values fall back to the login form
- shows a loading mask while redirecting, and restores the form with a
  warning when the provider has no redirect URL configured
- `?admin` still wins, so /login?admin&sso=oidc keeps local login
@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The login page now reads the sso query parameter, selects a supported provider, and redirects automatically. It shows a spinner during redirection, restores the form on failure, and stores CAS state before CAS navigation.

Changes

Automatic SSO redirection

Layer / File(s) Summary
SSO provider selection
src/pages/login/index.tsx
Recognized sso values select redirect URL getters. The admin value disables automatic SSO redirection.
Redirect execution and loading state
src/pages/login/index.tsx
The page resolves provider URLs, stores CAS state, navigates to the returned URL, handles missing data or errors, and displays a loading spinner during redirection.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant LoginPage
  participant SSOURLGetter
  participant Browser
  LoginPage->>SSOURLGetter: Resolve provider redirect URL
  SSOURLGetter-->>LoginPage: Return redirect URL and optional CAS state
  LoginPage->>Browser: Store CAS state when present
  LoginPage->>Browser: Navigate to redirect URL
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes automatic SSO redirection for the login page and matches the main change.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/login-sso-auto-redirect

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/pages/login/index.tsx (1)

66-66: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Replace Promise<any> with a redirect response type.

any prevents TypeScript from checking the string and CAS object shapes in res.dat. Define an explicit union or interface for these responses.

As per coding guidelines, “avoid any.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/login/index.tsx` at line 66, Replace the Promise<any> return type
in SSO_REDIRECT_GETTERS with an explicit redirect response type covering the
supported string and CAS object shapes returned in res.dat, so TypeScript
validates both forms without using any.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/pages/login/index.tsx`:
- Around line 127-129: Update the SSO redirect request’s catch handler to both
reset ssoRedirecting and display the existing sso_no_url warning when redirect
resolution fails. Reuse the component’s established warning/notification
mechanism and preserve the form-restoration behavior.
- Around line 82-85: Update the SSO redirect effect around ssoWay and
SSO_REDIRECT_GETTERS to depend on both ssoWay and redirect rather than only the
initial values. Set ssoRedirecting to false when no recognized provider exists,
and guard asynchronous redirect results with a cleanup-stale flag so outdated
promises cannot update state after query changes or unmount.

---

Nitpick comments:
In `@src/pages/login/index.tsx`:
- Line 66: Replace the Promise<any> return type in SSO_REDIRECT_GETTERS with an
explicit redirect response type covering the supported string and CAS object
shapes returned in res.dat, so TypeScript validates both forms without using
any.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: affc8265-629d-43be-962f-279dfa6a20fe

📥 Commits

Reviewing files that changed from the base of the PR and between ded15bf and acbdb41.

📒 Files selected for processing (1)
  • src/pages/login/index.tsx

Comment thread src/pages/login/index.tsx
Comment on lines +82 to +85
const searchParams = new URLSearchParams(location.search);
// 带 ?admin 表示要用本地账号登录,此时不自动跳转,与 Plus 侧全局自动跳转的约定保持一致
const ssoWay = searchParams.has('admin') ? '' : (searchParams.get('sso') || '').toLowerCase();
const [ssoRedirecting, setSsoRedirecting] = useState(!!SSO_REDIRECT_GETTERS[ssoWay]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- file outline ---'
ast-grep outline src/pages/login/index.tsx
printf '%s\n' '--- relevant source ---'
sed -n '1,180p' src/pages/login/index.tsx
printf '%s\n' '--- focused diff ---'
git diff -- src/pages/login/index.tsx
printf '%s\n' '--- related symbols ---'
rg -n -C 3 'SSO_REDIRECT_GETTERS|ssoWay|ssoRedirecting|location\.search|redirect' src/pages/login/index.tsx

Repository: n9e/fe

Length of output: 10385


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- effect and render sections ---'
sed -n '76,138p' src/pages/login/index.tsx
sed -n '185,205p' src/pages/login/index.tsx
printf '%s\n' '--- login route usage ---'
rg -n -C 3 'pages/login|Login\b|path=.*login|/login' src --glob '*.{ts,tsx}'
printf '%s\n' '--- redirect service signatures ---'
rg -n -C 4 'export (async )?function getRedirect|const getRedirect|function getRedirect' src
printf '%s\n' '--- React/router versions ---'
rg -n '"(react|react-router|react-router-dom)"' package.json */package.json 2>/dev/null || true
printf '%s\n' '--- standalone lifecycle probe ---'
node - <<'JS'
const getters = { oidc: () => Promise.resolve({ dat: 'oidc-url' }) };
let state = false;
let currentQuery = { sso: 'oidc', redirect: '/first' };

function render(query) {
  const ssoWay = query.sso || '';
  const redirect = query.redirect || '';
  const effect = () => {
    const getter = getters[ssoWay];
    if (!getter) return;
    getter(redirect).then((res) => {
      if (res.dat) {
        console.log(`redirect=${res.dat}, requested=${redirect}`);
      }
    });
  };
  return { effect, initialLoading: !!getters[ssoWay] };
}

const first = render(currentQuery);
state = first.initialLoading;
currentQuery = { sso: 'oidc', redirect: '/second' };
const second = render(currentQuery);
console.log(`state-after-query-change=${state}`);
first.effect(); // Models an effect with [] retaining its first render's closure.
console.log(`new-render-effect-created=${typeof second.effect === 'function'}`);
JS

Repository: n9e/fe

Length of output: 28205


React to later login query changes.

The empty dependency array captures the initial ssoWay and redirect. Add both dependencies, synchronize ssoRedirecting for recognized and unrecognized providers, and ignore stale promise results during cleanup.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/login/index.tsx` around lines 82 - 85, Update the SSO redirect
effect around ssoWay and SSO_REDIRECT_GETTERS to depend on both ssoWay and
redirect rather than only the initial values. Set ssoRedirecting to false when
no recognized provider exists, and guard asynchronous redirect results with a
cleanup-stale flag so outdated promises cannot update state after query changes
or unmount.

Comment thread src/pages/login/index.tsx
Comment on lines +127 to +129
.catch(() => {
setSsoRedirecting(false);
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Show the SSO failure warning on request errors.

This path restores the form but does not display sso_no_url. The PR requires the existing message when redirect resolution fails.

Proposed fix
       .catch(() => {
+        message.warning(t('sso_no_url', { name: ssoWay }));
         setSsoRedirecting(false);
       });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.catch(() => {
setSsoRedirecting(false);
});
.catch(() => {
message.warning(t('sso_no_url', { name: ssoWay }));
setSsoRedirecting(false);
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/login/index.tsx` around lines 127 - 129, Update the SSO redirect
request’s catch handler to both reset ssoRedirecting and display the existing
sso_no_url warning when redirect resolution fails. Reuse the component’s
established warning/notification mechanism and preserve the form-restoration
behavior.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant