mirror of
https://github.com/logto-io/logto.git
synced 2024-12-30 20:33:54 -05:00
chore: remove dev feature guard for first screens (#6522)
* chore: remove dev feature guard for new first screens * chore: add changeset * chore(experience): add comments for identifier sign-in and register page
This commit is contained in:
parent
652898f978
commit
3d3a220306
6 changed files with 84 additions and 41 deletions
30
.changeset/nine-vans-whisper.md
Normal file
30
.changeset/nine-vans-whisper.md
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
---
|
||||||
|
"@logto/integration-tests": minor
|
||||||
|
"@logto/experience": minor
|
||||||
|
"@logto/core": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
add support for additional first screen options
|
||||||
|
|
||||||
|
This feature introduces new first screen options, allowing developers to customize the initial screen presented to users. In addition to the existing `sign_in` and `register` options, the following first screen choices are now supported:
|
||||||
|
|
||||||
|
- `identifier:sign_in`: Only display specific identifier-based sign-in methods to users.
|
||||||
|
- `identifier:register`: Only display specific identifier-based registration methods to users.
|
||||||
|
- `reset_password`: Allow users to directly access the password reset page.
|
||||||
|
- `single_sign_on`: Allow users to directly access the single sign-on (SSO) page.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Example usage (React project using React SDK)
|
||||||
|
void signIn({
|
||||||
|
redirectUri,
|
||||||
|
firstScreen: 'identifier:sign_in',
|
||||||
|
/**
|
||||||
|
* Optional. Specifies which sign-in methods to display on the identifier sign-in page.
|
||||||
|
* If not specified, the default sign-in experience configuration will be used.
|
||||||
|
* This option is effective when the `firstScreen` value is `identifier:sign_in`, `identifier:register`, or `reset_password`.
|
||||||
|
*/
|
||||||
|
identifiers: ['email', 'phone'],
|
||||||
|
});
|
||||||
|
```
|
|
@ -12,9 +12,7 @@ import {
|
||||||
import { conditional } from '@silverhand/essentials';
|
import { conditional } from '@silverhand/essentials';
|
||||||
import { type AllClientMetadata, type ClientAuthMethod, errors } from 'oidc-provider';
|
import { type AllClientMetadata, type ClientAuthMethod, errors } from 'oidc-provider';
|
||||||
|
|
||||||
import { EnvSet } from '#src/env-set/index.js';
|
import { type EnvSet } from '#src/env-set/index.js';
|
||||||
|
|
||||||
const { isDevFeaturesEnabled } = EnvSet.values;
|
|
||||||
|
|
||||||
export const getConstantClientMetadata = (
|
export const getConstantClientMetadata = (
|
||||||
envSet: EnvSet,
|
envSet: EnvSet,
|
||||||
|
@ -91,19 +89,15 @@ export const getUtcStartOfTheDay = (date: Date) => {
|
||||||
const firstScreenRouteMapping: Record<FirstScreen, keyof typeof experience.routes> = {
|
const firstScreenRouteMapping: Record<FirstScreen, keyof typeof experience.routes> = {
|
||||||
[FirstScreen.SignIn]: 'signIn',
|
[FirstScreen.SignIn]: 'signIn',
|
||||||
[FirstScreen.Register]: 'register',
|
[FirstScreen.Register]: 'register',
|
||||||
/**
|
[FirstScreen.ResetPassword]: 'resetPassword',
|
||||||
* Todo @xiaoyijun remove isDevFeaturesEnabled check
|
[FirstScreen.IdentifierSignIn]: 'identifierSignIn',
|
||||||
* Fallback to signIn when dev feature is not ready (these three screens are not supported yet)
|
[FirstScreen.IdentifierRegister]: 'identifierRegister',
|
||||||
*/
|
[FirstScreen.SingleSignOn]: 'sso',
|
||||||
[FirstScreen.ResetPassword]: isDevFeaturesEnabled ? 'resetPassword' : 'signIn',
|
|
||||||
[FirstScreen.IdentifierSignIn]: isDevFeaturesEnabled ? 'identifierSignIn' : 'signIn',
|
|
||||||
[FirstScreen.IdentifierRegister]: isDevFeaturesEnabled ? 'identifierRegister' : 'signIn',
|
|
||||||
[FirstScreen.SingleSignOn]: isDevFeaturesEnabled ? 'sso' : 'signIn',
|
|
||||||
[FirstScreen.SignInDeprecated]: 'signIn',
|
[FirstScreen.SignInDeprecated]: 'signIn',
|
||||||
};
|
};
|
||||||
|
|
||||||
// Note: this eslint comment can be removed once the dev feature flag is removed
|
// Note: this eslint comment can be removed once the dev feature flag is removed
|
||||||
// eslint-disable-next-line complexity
|
|
||||||
export const buildLoginPromptUrl = (params: ExtraParamsObject, appId?: unknown): string => {
|
export const buildLoginPromptUrl = (params: ExtraParamsObject, appId?: unknown): string => {
|
||||||
const firstScreenKey =
|
const firstScreenKey =
|
||||||
params[ExtraParamsKey.FirstScreen] ??
|
params[ExtraParamsKey.FirstScreen] ??
|
||||||
|
@ -131,11 +125,8 @@ export const buildLoginPromptUrl = (params: ExtraParamsObject, appId?: unknown):
|
||||||
searchParams.append(ExtraParamsKey.LoginHint, params[ExtraParamsKey.LoginHint]);
|
searchParams.append(ExtraParamsKey.LoginHint, params[ExtraParamsKey.LoginHint]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isDevFeaturesEnabled) {
|
if (params[ExtraParamsKey.Identifier]) {
|
||||||
// eslint-disable-next-line unicorn/no-lonely-if
|
searchParams.append(ExtraParamsKey.Identifier, params[ExtraParamsKey.Identifier]);
|
||||||
if (params[ExtraParamsKey.Identifier]) {
|
|
||||||
searchParams.append(ExtraParamsKey.Identifier, params[ExtraParamsKey.Identifier]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (directSignIn) {
|
if (directSignIn) {
|
||||||
|
|
|
@ -7,7 +7,6 @@ import LoadingLayerProvider from './Providers/LoadingLayerProvider';
|
||||||
import PageContextProvider from './Providers/PageContextProvider';
|
import PageContextProvider from './Providers/PageContextProvider';
|
||||||
import SettingsProvider from './Providers/SettingsProvider';
|
import SettingsProvider from './Providers/SettingsProvider';
|
||||||
import UserInteractionContextProvider from './Providers/UserInteractionContextProvider';
|
import UserInteractionContextProvider from './Providers/UserInteractionContextProvider';
|
||||||
import { isDevFeaturesEnabled } from './constants/env';
|
|
||||||
import Callback from './pages/Callback';
|
import Callback from './pages/Callback';
|
||||||
import Consent from './pages/Consent';
|
import Consent from './pages/Consent';
|
||||||
import Continue from './pages/Continue';
|
import Continue from './pages/Continue';
|
||||||
|
@ -119,7 +118,7 @@ const App = () => {
|
||||||
{/* Single sign-on */}
|
{/* Single sign-on */}
|
||||||
<Route path={experience.routes.sso}>
|
<Route path={experience.routes.sso}>
|
||||||
{/* Single sign-on first screen landing page */}
|
{/* Single sign-on first screen landing page */}
|
||||||
{isDevFeaturesEnabled && <Route index element={<SingleSignOnLanding />} />}
|
<Route index element={<SingleSignOnLanding />} />
|
||||||
<Route path="email" element={<SingleSignOnEmail />} />
|
<Route path="email" element={<SingleSignOnEmail />} />
|
||||||
<Route path="connectors" element={<SingleSignOnConnectors />} />
|
<Route path="connectors" element={<SingleSignOnConnectors />} />
|
||||||
</Route>
|
</Route>
|
||||||
|
@ -127,27 +126,32 @@ const App = () => {
|
||||||
{/* Consent */}
|
{/* Consent */}
|
||||||
<Route path="consent" element={<Consent />} />
|
<Route path="consent" element={<Consent />} />
|
||||||
|
|
||||||
{isDevFeaturesEnabled && (
|
{/*
|
||||||
<>
|
* Identifier sign-in (first screen)
|
||||||
{/* Identifier sign-in */}
|
* The first screen which only display specific identifier-based sign-in methods to users
|
||||||
<Route
|
*/}
|
||||||
path={experience.routes.identifierSignIn}
|
<Route
|
||||||
element={<IdentifierSignIn />}
|
path={experience.routes.identifierSignIn}
|
||||||
/>
|
element={<IdentifierSignIn />}
|
||||||
|
/>
|
||||||
|
|
||||||
{/* Identifier register */}
|
{/*
|
||||||
<Route
|
* Identifier register (first screen)
|
||||||
path={experience.routes.identifierRegister}
|
* The first screen which only display specific identifier-based registration methods to users
|
||||||
element={<IdentifierRegister />}
|
*/}
|
||||||
/>
|
<Route
|
||||||
|
path={experience.routes.identifierRegister}
|
||||||
|
element={<IdentifierRegister />}
|
||||||
|
/>
|
||||||
|
|
||||||
{/* Reset password */}
|
{/*
|
||||||
<Route
|
* Reset password (first screen)
|
||||||
path={experience.routes.resetPassword}
|
* The first screen which allow users to directly access the password reset page
|
||||||
element={<ResetPasswordLanding />}
|
*/}
|
||||||
/>
|
<Route
|
||||||
</>
|
path={experience.routes.resetPassword}
|
||||||
)}
|
element={<ResetPasswordLanding />}
|
||||||
|
/>
|
||||||
|
|
||||||
<Route path="*" element={<ErrorPage />} />
|
<Route path="*" element={<ErrorPage />} />
|
||||||
</Route>
|
</Route>
|
||||||
|
|
|
@ -10,6 +10,15 @@ import { identifierInputDescriptionMap } from '@/utils/form';
|
||||||
|
|
||||||
import useIdentifierSignUpMethods from './use-identifier-sign-up-methods';
|
import useIdentifierSignUpMethods from './use-identifier-sign-up-methods';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identifier register page
|
||||||
|
*
|
||||||
|
* This page is used to display specific identifier-based registration methods to users.
|
||||||
|
*
|
||||||
|
* This page can be used as the first screen of the authentication flow,
|
||||||
|
* and can be configured by setting the `first_screen` parameter to `identifier:register`
|
||||||
|
* in the authentication URL.
|
||||||
|
*/
|
||||||
const IdentifierRegister = () => {
|
const IdentifierRegister = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const signUpMethods = useIdentifierSignUpMethods();
|
const signUpMethods = useIdentifierSignUpMethods();
|
||||||
|
|
|
@ -11,6 +11,18 @@ import { identifierInputDescriptionMap } from '@/utils/form';
|
||||||
|
|
||||||
import useIdentifierSignInMethods from './use-identifier-sign-in-methods';
|
import useIdentifierSignInMethods from './use-identifier-sign-in-methods';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identifier sign-in page
|
||||||
|
*
|
||||||
|
* This page is used to display specific identifier-based sign-in methods to users.
|
||||||
|
*
|
||||||
|
* This page can be used as the first screen of the authentication flow,
|
||||||
|
* and can be configured by setting the `first_screen` parameter to `identifier:sign_in`
|
||||||
|
* in the authentication URL.
|
||||||
|
*
|
||||||
|
* identifiers used in this page can be configured by setting the `identifier` parameter
|
||||||
|
* in the authentication URL, multiple identifiers can be separated by space.
|
||||||
|
*/
|
||||||
const IdentifierSignIn = () => {
|
const IdentifierSignIn = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
|
|
@ -10,9 +10,6 @@ import {
|
||||||
setSmsConnector,
|
setSmsConnector,
|
||||||
} from '#src/helpers/connector.js';
|
} from '#src/helpers/connector.js';
|
||||||
import ExpectExperience from '#src/ui-helpers/expect-experience.js';
|
import ExpectExperience from '#src/ui-helpers/expect-experience.js';
|
||||||
import { devFeatureTest } from '#src/utils.js';
|
|
||||||
|
|
||||||
const { describe, it } = devFeatureTest;
|
|
||||||
|
|
||||||
describe('first screen', () => {
|
describe('first screen', () => {
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
|
|
Loading…
Reference in a new issue