diff --git a/packages/experience/src/pages/IdentifierRegister/index.tsx b/packages/experience/src/pages/IdentifierRegister/index.tsx
index 37c8bca8b..41c2be9d0 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 4fe5913f3..a3457d492 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 520b8fd12..7d0583d56 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');
+ });
});
});