From 4ecd4b35f1c75db65a05a09deea270ffa21db614 Mon Sep 17 00:00:00 2001
From: wolfkill <27876473+wolfkill@users.noreply.github.com>
Date: Mon, 18 May 2026 15:12:55 +0800
Subject: [PATCH] fix: use members SSO endpoints for webapp auth
---
.../external-member-sso-auth.spec.tsx | 84 +++++++++++++++++++
.../components/external-member-sso-auth.tsx | 8 +-
2 files changed, 88 insertions(+), 4 deletions(-)
create mode 100644 web/app/(shareLayout)/webapp-signin/components/__tests__/external-member-sso-auth.spec.tsx
diff --git a/web/app/(shareLayout)/webapp-signin/components/__tests__/external-member-sso-auth.spec.tsx b/web/app/(shareLayout)/webapp-signin/components/__tests__/external-member-sso-auth.spec.tsx
new file mode 100644
index 00000000000000..e5ed112e5f0e39
--- /dev/null
+++ b/web/app/(shareLayout)/webapp-signin/components/__tests__/external-member-sso-auth.spec.tsx
@@ -0,0 +1,84 @@
+import { useSuspenseQuery } from '@tanstack/react-query'
+import { render, waitFor } from '@testing-library/react'
+import {
+ fetchMembersOAuth2SSOUrl,
+ fetchMembersOIDCSSOUrl,
+ fetchMembersSAMLSSOUrl,
+ fetchWebOAuth2SSOUrl,
+ fetchWebOIDCSSOUrl,
+ fetchWebSAMLSSOUrl,
+} from '@/service/share'
+import { SSOProtocol } from '@/types/feature'
+import ExternalMemberSSOAuth from '../external-member-sso-auth'
+
+const mockPush = vi.fn()
+const mockUseSearchParams = vi.fn()
+
+vi.mock('@tanstack/react-query', () => ({
+ useSuspenseQuery: vi.fn(),
+}))
+
+vi.mock('@/app/components/base/app-unavailable', () => ({
+ default: () =>
,
+}))
+
+vi.mock('@/app/components/base/loading', () => ({
+ default: () => ,
+}))
+
+vi.mock('@/next/navigation', () => ({
+ useRouter: () => ({ push: mockPush }),
+ useSearchParams: () => mockUseSearchParams(),
+}))
+
+vi.mock('@/service/share', () => ({
+ fetchMembersOAuth2SSOUrl: vi.fn(),
+ fetchMembersOIDCSSOUrl: vi.fn(),
+ fetchMembersSAMLSSOUrl: vi.fn(),
+ fetchWebOAuth2SSOUrl: vi.fn(),
+ fetchWebOIDCSSOUrl: vi.fn(),
+ fetchWebSAMLSSOUrl: vi.fn(),
+}))
+
+vi.mock('@/service/system-features', () => ({
+ systemFeaturesQueryOptions: () => ({}),
+}))
+
+const mockUseSuspenseQuery = vi.mocked(useSuspenseQuery)
+
+const setSSOProtocol = (protocol: SSOProtocol) => {
+ mockUseSuspenseQuery.mockReturnValue({
+ data: {
+ webapp_auth: {
+ sso_config: {
+ protocol,
+ },
+ },
+ },
+ } as ReturnType)
+}
+
+describe('ExternalMemberSSOAuth', () => {
+ beforeEach(() => {
+ vi.clearAllMocks()
+ mockUseSearchParams.mockReturnValue(new URLSearchParams('redirect_url=%2Fchat%2Fapp-code'))
+ })
+
+ it.each([
+ [SSOProtocol.SAML, fetchMembersSAMLSSOUrl, fetchWebSAMLSSOUrl],
+ [SSOProtocol.OIDC, fetchMembersOIDCSSOUrl, fetchWebOIDCSSOUrl],
+ [SSOProtocol.OAuth2, fetchMembersOAuth2SSOUrl, fetchWebOAuth2SSOUrl],
+ ])('uses members SSO endpoint for external webapp %s auth', async (protocol, membersFetch, webFetch) => {
+ setSSOProtocol(protocol)
+ vi.mocked(membersFetch).mockResolvedValue({ url: `https://app.example/${protocol}` })
+ vi.mocked(webFetch).mockResolvedValue({ url: `https://console.example/${protocol}` })
+
+ render()
+
+ await waitFor(() => {
+ expect(membersFetch).toHaveBeenCalledWith('app-code', '/chat/app-code')
+ })
+ expect(webFetch).not.toHaveBeenCalled()
+ expect(mockPush).toHaveBeenCalledWith(`https://app.example/${protocol}`)
+ })
+})
diff --git a/web/app/(shareLayout)/webapp-signin/components/external-member-sso-auth.tsx b/web/app/(shareLayout)/webapp-signin/components/external-member-sso-auth.tsx
index 003aab4cabab24..e7d2804930b625 100644
--- a/web/app/(shareLayout)/webapp-signin/components/external-member-sso-auth.tsx
+++ b/web/app/(shareLayout)/webapp-signin/components/external-member-sso-auth.tsx
@@ -6,7 +6,7 @@ import { useCallback, useEffect } from 'react'
import AppUnavailable from '@/app/components/base/app-unavailable'
import Loading from '@/app/components/base/loading'
import { useRouter, useSearchParams } from '@/next/navigation'
-import { fetchWebOAuth2SSOUrl, fetchWebOIDCSSOUrl, fetchWebSAMLSSOUrl } from '@/service/share'
+import { fetchMembersOAuth2SSOUrl, fetchMembersOIDCSSOUrl, fetchMembersSAMLSSOUrl } from '@/service/share'
import { systemFeaturesQueryOptions } from '@/service/system-features'
import { SSOProtocol } from '@/types/feature'
@@ -41,17 +41,17 @@ const ExternalMemberSSOAuth = () => {
switch (systemFeatures.webapp_auth.sso_config.protocol) {
case SSOProtocol.SAML: {
- const samlRes = await fetchWebSAMLSSOUrl(appCode, redirectUrl)
+ const samlRes = await fetchMembersSAMLSSOUrl(appCode, redirectUrl)
router.push(samlRes.url)
break
}
case SSOProtocol.OIDC: {
- const oidcRes = await fetchWebOIDCSSOUrl(appCode, redirectUrl)
+ const oidcRes = await fetchMembersOIDCSSOUrl(appCode, redirectUrl)
router.push(oidcRes.url)
break
}
case SSOProtocol.OAuth2: {
- const oauth2Res = await fetchWebOAuth2SSOUrl(appCode, redirectUrl)
+ const oauth2Res = await fetchMembersOAuth2SSOUrl(appCode, redirectUrl)
router.push(oauth2Res.url)
break
}