mirror of
https://github.com/logto-io/logto.git
synced 2024-12-30 20:33:54 -05:00
fix(experience): correct first screen fallbacks (#6472)
This commit is contained in:
parent
608d2efdb5
commit
d4674833c8
4 changed files with 54 additions and 4 deletions
|
@ -1,9 +1,10 @@
|
||||||
import { AgreeToTermsPolicy, experience } from '@logto/schemas';
|
import { AgreeToTermsPolicy, experience, SignInMode } from '@logto/schemas';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Navigate } from 'react-router-dom';
|
import { Navigate } from 'react-router-dom';
|
||||||
|
|
||||||
import FocusedAuthPageLayout from '@/Layout/FocusedAuthPageLayout';
|
import FocusedAuthPageLayout from '@/Layout/FocusedAuthPageLayout';
|
||||||
import IdentifierRegisterForm from '@/components/IdentifierRegisterForm';
|
import IdentifierRegisterForm from '@/components/IdentifierRegisterForm';
|
||||||
|
import { useSieMethods } from '@/hooks/use-sie';
|
||||||
import { identifierInputDescriptionMap } from '@/utils/form';
|
import { identifierInputDescriptionMap } from '@/utils/form';
|
||||||
|
|
||||||
import useIdentifierSignUpMethods from './use-identifier-sign-up-methods';
|
import useIdentifierSignUpMethods from './use-identifier-sign-up-methods';
|
||||||
|
@ -11,11 +12,14 @@ import useIdentifierSignUpMethods from './use-identifier-sign-up-methods';
|
||||||
const IdentifierRegister = () => {
|
const IdentifierRegister = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const signUpMethods = useIdentifierSignUpMethods();
|
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 <Navigate to={`/${experience.routes.signIn}`} />;
|
return <Navigate to={`/${experience.routes.signIn}`} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,22 @@
|
||||||
import { AgreeToTermsPolicy, experience } from '@logto/schemas';
|
import { AgreeToTermsPolicy, experience } from '@logto/schemas';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { Navigate } from 'react-router-dom';
|
||||||
|
|
||||||
import FocusedAuthPageLayout from '@/Layout/FocusedAuthPageLayout';
|
import FocusedAuthPageLayout from '@/Layout/FocusedAuthPageLayout';
|
||||||
import SingleSignOnForm from '@/components/SingleSignOnForm';
|
import SingleSignOnForm from '@/components/SingleSignOnForm';
|
||||||
|
import { useSieMethods } from '@/hooks/use-sie';
|
||||||
import useTerms from '@/hooks/use-terms';
|
import useTerms from '@/hooks/use-terms';
|
||||||
|
|
||||||
const SingleSignOnLanding = () => {
|
const SingleSignOnLanding = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const { singleSignOnEnabled } = useSieMethods();
|
||||||
const { agreeToTermsPolicy } = useTerms();
|
const { agreeToTermsPolicy } = useTerms();
|
||||||
|
|
||||||
|
// Fallback to sign-in page if SSO is not enabled
|
||||||
|
if (!singleSignOnEnabled) {
|
||||||
|
return <Navigate to={`/${experience.routes.signIn}`} />;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FocusedAuthPageLayout
|
<FocusedAuthPageLayout
|
||||||
pageMeta={{ titleKey: 'action.single_sign_on' }}
|
pageMeta={{ titleKey: 'action.single_sign_on' }}
|
||||||
|
|
|
@ -41,6 +41,11 @@ export const patchSsoConnectorById = async (id: string, data: Partial<SsoConnect
|
||||||
})
|
})
|
||||||
.json<SsoConnectorWithProviderConfig>();
|
.json<SsoConnectorWithProviderConfig>();
|
||||||
|
|
||||||
|
export const clearSsoConnectors = async () => {
|
||||||
|
const connectors = await getSsoConnectors();
|
||||||
|
await Promise.all(connectors.map(async (connector) => deleteSsoConnectorById(connector.id)));
|
||||||
|
};
|
||||||
|
|
||||||
export class SsoConnectorApi {
|
export class SsoConnectorApi {
|
||||||
readonly connectorInstances = new Map<string, SsoConnector>();
|
readonly connectorInstances = new Map<string, SsoConnector>();
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import { ConnectorType } from '@logto/connector-kit';
|
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 { updateSignInExperience } from '#src/api/sign-in-experience.js';
|
||||||
|
import { clearSsoConnectors } from '#src/api/sso-connector.js';
|
||||||
import { demoAppUrl } from '#src/constants.js';
|
import { demoAppUrl } from '#src/constants.js';
|
||||||
import {
|
import {
|
||||||
clearConnectorsByTypes,
|
clearConnectorsByTypes,
|
||||||
|
@ -16,8 +17,12 @@ const { describe, it } = devFeatureTest;
|
||||||
describe('first screen', () => {
|
describe('first screen', () => {
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await clearConnectorsByTypes([ConnectorType.Social, ConnectorType.Email, ConnectorType.Sms]);
|
await clearConnectorsByTypes([ConnectorType.Social, ConnectorType.Email, ConnectorType.Sms]);
|
||||||
|
await clearSsoConnectors();
|
||||||
await setEmailConnector();
|
await setEmailConnector();
|
||||||
await setSmsConnector();
|
await setSmsConnector();
|
||||||
|
await updateSignInExperience({
|
||||||
|
signInMode: SignInMode.SignInAndRegister,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('sign-in page', () => {
|
describe('sign-in page', () => {
|
||||||
|
@ -44,6 +49,9 @@ describe('first screen', () => {
|
||||||
|
|
||||||
describe('single sign-on page', () => {
|
describe('single sign-on page', () => {
|
||||||
it('should be landed on single sign-on page directly', async () => {
|
it('should be landed on single sign-on page directly', async () => {
|
||||||
|
await updateSignInExperience({
|
||||||
|
singleSignOnEnabled: true,
|
||||||
|
});
|
||||||
const experience = new ExpectExperience(await browser.newPage());
|
const experience = new ExpectExperience(await browser.newPage());
|
||||||
const url = new URL(demoAppUrl);
|
const url = new URL(demoAppUrl);
|
||||||
url.searchParams.set('first_screen', 'single_sign_on');
|
url.searchParams.set('first_screen', 'single_sign_on');
|
||||||
|
@ -51,6 +59,19 @@ describe('first screen', () => {
|
||||||
experience.toBeAt('single-sign-on');
|
experience.toBeAt('single-sign-on');
|
||||||
await experience.page.close();
|
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', () => {
|
describe('identifier sign-in page', () => {
|
||||||
|
@ -84,6 +105,7 @@ describe('first screen', () => {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
signInMode: SignInMode.SignIn,
|
||||||
});
|
});
|
||||||
|
|
||||||
// eslint-disable-next-line @silverhand/fp/no-mutation
|
// eslint-disable-next-line @silverhand/fp/no-mutation
|
||||||
|
@ -161,6 +183,7 @@ describe('first screen', () => {
|
||||||
password: false,
|
password: false,
|
||||||
verify: true,
|
verify: true,
|
||||||
},
|
},
|
||||||
|
signInMode: SignInMode.SignInAndRegister,
|
||||||
});
|
});
|
||||||
|
|
||||||
// eslint-disable-next-line @silverhand/fp/no-mutation
|
// eslint-disable-next-line @silverhand/fp/no-mutation
|
||||||
|
@ -197,5 +220,15 @@ describe('first screen', () => {
|
||||||
await experience.page.goto(url.href, { waitUntil: 'networkidle0' });
|
await experience.page.goto(url.href, { waitUntil: 'networkidle0' });
|
||||||
experience.toBeAt('sign-in');
|
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');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue