diff --git a/packages/experience/src/pages/IdentifierRegister/index.tsx b/packages/experience/src/pages/IdentifierRegister/index.tsx index 37c8bca8b46..41c2be9d043 100644 --- a/packages/experience/src/pages/IdentifierRegister/index.tsx +++ b/packages/experience/src/pages/IdentifierRegister/index.tsx @@ -1,9 +1,10 @@ -import { AgreeToTermsPolicy, experience } from '@logto/schemas'; +import { AgreeToTermsPolicy, experience, SignInMode } from '@logto/schemas'; import { useTranslation } from 'react-i18next'; import { Navigate } from 'react-router-dom'; import FocusedAuthPageLayout from '@/Layout/FocusedAuthPageLayout'; import IdentifierRegisterForm from '@/components/IdentifierRegisterForm'; +import { useSieMethods } from '@/hooks/use-sie'; import { identifierInputDescriptionMap } from '@/utils/form'; import useIdentifierSignUpMethods from './use-identifier-sign-up-methods'; @@ -11,11 +12,14 @@ import useIdentifierSignUpMethods from './use-identifier-sign-up-methods'; const IdentifierRegister = () => { const { t } = useTranslation(); const signUpMethods = useIdentifierSignUpMethods(); + const { signInMode } = useSieMethods(); /** - * Fallback to sign-in page if no sign up methods are available (not allowed to create an account). + * Fallback to sign-in page in the following cases: + * - Sign-in mode is set to `SignIn` (user registration is not enabled in the sign-in experience configuration) + * - No sign up methods are available */ - if (signUpMethods.length === 0) { + if (signInMode === SignInMode.SignIn || signUpMethods.length === 0) { return ; } diff --git a/packages/experience/src/pages/SingleSignOnLanding/index.tsx b/packages/experience/src/pages/SingleSignOnLanding/index.tsx index 4fe5913f343..a3457d49233 100644 --- a/packages/experience/src/pages/SingleSignOnLanding/index.tsx +++ b/packages/experience/src/pages/SingleSignOnLanding/index.tsx @@ -1,14 +1,22 @@ import { AgreeToTermsPolicy, experience } from '@logto/schemas'; import { useTranslation } from 'react-i18next'; +import { Navigate } from 'react-router-dom'; import FocusedAuthPageLayout from '@/Layout/FocusedAuthPageLayout'; import SingleSignOnForm from '@/components/SingleSignOnForm'; +import { useSieMethods } from '@/hooks/use-sie'; import useTerms from '@/hooks/use-terms'; const SingleSignOnLanding = () => { const { t } = useTranslation(); + const { singleSignOnEnabled } = useSieMethods(); const { agreeToTermsPolicy } = useTerms(); + // Fallback to sign-in page if SSO is not enabled + if (!singleSignOnEnabled) { + return ; + } + return ( (); +export const clearSsoConnectors = async () => { + const connectors = await getSsoConnectors(); + await Promise.all(connectors.map(async (connector) => deleteSsoConnectorById(connector.id))); +}; + export class SsoConnectorApi { readonly connectorInstances = new Map(); diff --git a/packages/integration-tests/src/tests/experience/first-screen.test.ts b/packages/integration-tests/src/tests/experience/first-screen.test.ts index 520b8fd1214..7d0583d564d 100644 --- a/packages/integration-tests/src/tests/experience/first-screen.test.ts +++ b/packages/integration-tests/src/tests/experience/first-screen.test.ts @@ -1,7 +1,8 @@ import { ConnectorType } from '@logto/connector-kit'; -import { SignInIdentifier } from '@logto/schemas'; +import { SignInIdentifier, SignInMode } from '@logto/schemas'; import { updateSignInExperience } from '#src/api/sign-in-experience.js'; +import { clearSsoConnectors } from '#src/api/sso-connector.js'; import { demoAppUrl } from '#src/constants.js'; import { clearConnectorsByTypes, @@ -16,8 +17,12 @@ const { describe, it } = devFeatureTest; describe('first screen', () => { beforeAll(async () => { await clearConnectorsByTypes([ConnectorType.Social, ConnectorType.Email, ConnectorType.Sms]); + await clearSsoConnectors(); await setEmailConnector(); await setSmsConnector(); + await updateSignInExperience({ + signInMode: SignInMode.SignInAndRegister, + }); }); describe('sign-in page', () => { @@ -44,6 +49,9 @@ describe('first screen', () => { describe('single sign-on page', () => { it('should be landed on single sign-on page directly', async () => { + await updateSignInExperience({ + singleSignOnEnabled: true, + }); const experience = new ExpectExperience(await browser.newPage()); const url = new URL(demoAppUrl); url.searchParams.set('first_screen', 'single_sign_on'); @@ -51,6 +59,19 @@ describe('first screen', () => { experience.toBeAt('single-sign-on'); await experience.page.close(); }); + + it('should fallback to sign-in page if SSO is not enabled', async () => { + // Turn off SSO + await updateSignInExperience({ + singleSignOnEnabled: false, + }); + const experience = new ExpectExperience(await browser.newPage()); + const url = new URL(demoAppUrl); + url.searchParams.set('first_screen', 'single_sign_on'); + await experience.page.goto(url.href, { waitUntil: 'networkidle0' }); + experience.toBeAt('sign-in'); + await experience.page.close(); + }); }); describe('identifier sign-in page', () => { @@ -84,6 +105,7 @@ describe('first screen', () => { }, ], }, + signInMode: SignInMode.SignIn, }); // eslint-disable-next-line @silverhand/fp/no-mutation @@ -161,6 +183,7 @@ describe('first screen', () => { password: false, verify: true, }, + signInMode: SignInMode.SignInAndRegister, }); // eslint-disable-next-line @silverhand/fp/no-mutation @@ -197,5 +220,15 @@ describe('first screen', () => { await experience.page.goto(url.href, { waitUntil: 'networkidle0' }); experience.toBeAt('sign-in'); }); + + it('should fallback to sign-in page if sign-in mode is `SignIn` only', async () => { + await updateSignInExperience({ + signUp: { identifiers: [SignInIdentifier.Email], password: false, verify: true }, + signInMode: SignInMode.SignIn, + }); + + await experience.page.goto(url.href, { waitUntil: 'networkidle0' }); + experience.toBeAt('sign-in'); + }); }); });