mirror of
https://github.com/logto-io/logto.git
synced 2025-03-17 22:31:28 -05:00
chore: remove old sign in methods (#2295)
This commit is contained in:
parent
19024719ec
commit
5e571936c9
23 changed files with 99 additions and 784 deletions
|
@ -1,5 +1,5 @@
|
|||
import type { SignInExperience } from '@logto/schemas';
|
||||
import { ConnectorType, SignInMethodState } from '@logto/schemas';
|
||||
import { SignUpIdentifier, SignInIdentifier, ConnectorType } from '@logto/schemas';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import type { RequestError } from './use-api';
|
||||
|
@ -12,11 +12,23 @@ const useConnectorInUse = (type?: ConnectorType, target?: string): boolean | und
|
|||
}
|
||||
|
||||
if (type === ConnectorType.Email) {
|
||||
return data.signInMethods.email !== SignInMethodState.Disabled;
|
||||
return (
|
||||
data.signIn.methods.some(
|
||||
({ identifier, verificationCode }) =>
|
||||
verificationCode && identifier === SignInIdentifier.Email
|
||||
) ||
|
||||
(data.signUp.identifier === SignUpIdentifier.Email && data.signUp.verify)
|
||||
);
|
||||
}
|
||||
|
||||
if (type === ConnectorType.Sms) {
|
||||
return data.signInMethods.sms !== SignInMethodState.Disabled;
|
||||
return (
|
||||
data.signIn.methods.some(
|
||||
({ identifier, verificationCode }) =>
|
||||
verificationCode && identifier === SignInIdentifier.Email
|
||||
) ||
|
||||
(data.signUp.identifier === SignUpIdentifier.Email && data.signUp.verify)
|
||||
);
|
||||
}
|
||||
|
||||
if (!target) {
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
import type { ConnectorResponse } from '@logto/schemas';
|
||||
import { ConnectorType, SignInMethodKey } from '@logto/schemas';
|
||||
import { useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import Alert from '@/components/Alert';
|
||||
import type { RequestError } from '@/hooks/use-api';
|
||||
|
||||
type Props = {
|
||||
method: SignInMethodKey;
|
||||
};
|
||||
|
||||
const ConnectorSetupWarning = ({ method }: Props) => {
|
||||
const { data: connectors } = useSWR<ConnectorResponse[], RequestError>('/api/connectors');
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
|
||||
const type = useMemo(() => {
|
||||
if (method === SignInMethodKey.Username) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (method === SignInMethodKey.Sms) {
|
||||
return ConnectorType.Sms;
|
||||
}
|
||||
|
||||
if (method === SignInMethodKey.Email) {
|
||||
return ConnectorType.Email;
|
||||
}
|
||||
|
||||
return ConnectorType.Social;
|
||||
}, [method]);
|
||||
|
||||
if (!type || !connectors) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (connectors.some(({ type: connectorType, enabled }) => connectorType === type && enabled)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Alert
|
||||
action="general.set_up"
|
||||
href={type === ConnectorType.Social ? '/connectors/social' : '/connectors'}
|
||||
>
|
||||
{t('sign_in_exp.setup_warning.no_connector', { context: type.toLowerCase() })}
|
||||
</Alert>
|
||||
);
|
||||
};
|
||||
|
||||
export default ConnectorSetupWarning;
|
|
@ -1,147 +0,0 @@
|
|||
import { SignInMethodKey } from '@logto/schemas';
|
||||
import { useMemo } from 'react';
|
||||
import { Controller, useFormContext } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import Checkbox from '@/components/Checkbox';
|
||||
import FormField from '@/components/FormField';
|
||||
import Select from '@/components/Select';
|
||||
import Switch from '@/components/Switch';
|
||||
|
||||
import type { SignInExperienceForm } from '../types';
|
||||
import ConnectorSetupWarning from './ConnectorSetupWarning';
|
||||
import ConnectorsTransfer from './ConnectorsTransfer';
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
const signInMethods = Object.values(SignInMethodKey);
|
||||
|
||||
const SignInMethodsForm = () => {
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
const { register, watch, control, getValues, setValue } = useFormContext<SignInExperienceForm>();
|
||||
const primaryMethod = watch('signInMethods.primary');
|
||||
const enableSecondary = watch('signInMethods.enableSecondary');
|
||||
const sms = watch('signInMethods.sms');
|
||||
const email = watch('signInMethods.email');
|
||||
const social = watch('signInMethods.social');
|
||||
|
||||
const postPrimaryMethodChange = (
|
||||
oldPrimaryMethod?: SignInMethodKey,
|
||||
primaryMethod?: SignInMethodKey
|
||||
) => {
|
||||
if (oldPrimaryMethod) {
|
||||
// The secondary sign-in method should select the old primary method by default.
|
||||
setValue(`signInMethods.${oldPrimaryMethod}`, true);
|
||||
}
|
||||
|
||||
if (primaryMethod) {
|
||||
// When one of the sign-in methods has been primary, it should not be able to be secondary simultaneously.
|
||||
setValue(`signInMethods.${primaryMethod}`, false);
|
||||
}
|
||||
};
|
||||
|
||||
const secondaryMethodsFields = useMemo(
|
||||
() =>
|
||||
signInMethods.map((method) => {
|
||||
const label = (
|
||||
<>
|
||||
{t('sign_in_exp.sign_in_methods.methods', { context: method })}
|
||||
{primaryMethod === method && (
|
||||
<span className={styles.primaryTag}>
|
||||
{t('sign_in_exp.sign_in_methods.methods_primary_tag')}
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
const enabled =
|
||||
(method === SignInMethodKey.Email && email) ||
|
||||
(method === SignInMethodKey.Sms && sms) ||
|
||||
(method === SignInMethodKey.Social && social);
|
||||
|
||||
return (
|
||||
<div key={method} className={styles.method}>
|
||||
<Controller
|
||||
name={`signInMethods.${method}`}
|
||||
control={control}
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<Checkbox
|
||||
label={label}
|
||||
disabled={primaryMethod === method}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
{enabled && <ConnectorSetupWarning method={method} />}
|
||||
</div>
|
||||
);
|
||||
}),
|
||||
[t, primaryMethod, email, sms, social, control]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={styles.title}>{t('sign_in_exp.sign_in_methods.title')}</div>
|
||||
<FormField title="sign_in_exp.sign_in_methods.primary">
|
||||
<Controller
|
||||
name="signInMethods.primary"
|
||||
control={control}
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<Select
|
||||
value={value}
|
||||
options={signInMethods.map((method) => ({
|
||||
value: method,
|
||||
title: t('sign_in_exp.sign_in_methods.methods', { context: method }),
|
||||
}))}
|
||||
onChange={(value) => {
|
||||
const oldPrimaryMethod = getValues('signInMethods.primary');
|
||||
onChange(value);
|
||||
postPrimaryMethodChange(oldPrimaryMethod, value);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</FormField>
|
||||
{primaryMethod && <ConnectorSetupWarning method={primaryMethod} />}
|
||||
{primaryMethod === SignInMethodKey.Social && (
|
||||
<div className={styles.primarySocial}>
|
||||
<Controller
|
||||
name="socialSignInConnectorTargets"
|
||||
control={control}
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<ConnectorsTransfer value={value} onChange={onChange} />
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<FormField title="sign_in_exp.sign_in_methods.enable_secondary">
|
||||
<Switch
|
||||
/**
|
||||
* DO NOT SET THIS FIELD TO REQUIRED UNLESS YOU KNOW WHAT YOU ARE DOING.
|
||||
* https://github.com/react-hook-form/react-hook-form/issues/2323
|
||||
*/
|
||||
{...register('signInMethods.enableSecondary')}
|
||||
label={t('sign_in_exp.sign_in_methods.enable_secondary_description')}
|
||||
/>
|
||||
</FormField>
|
||||
{enableSecondary && (
|
||||
<>
|
||||
{secondaryMethodsFields}
|
||||
{social && (
|
||||
<FormField title="sign_in_exp.sign_in_methods.define_social_methods">
|
||||
<Controller
|
||||
name="socialSignInConnectorTargets"
|
||||
control={control}
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<ConnectorsTransfer value={value} onChange={onChange} />
|
||||
)}
|
||||
/>
|
||||
</FormField>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default SignInMethodsForm;
|
|
@ -1,55 +0,0 @@
|
|||
import type { SignInExperience } from '@logto/schemas';
|
||||
import { SignInMethodKey, SignInMethodState } from '@logto/schemas';
|
||||
import { useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import UnnamedTrans from '@/components/UnnamedTrans';
|
||||
import useConnectorGroups from '@/hooks/use-connector-groups';
|
||||
|
||||
import * as styles from './SignInMethodsChangePreview.module.scss';
|
||||
|
||||
type Props = {
|
||||
data: SignInExperience;
|
||||
};
|
||||
|
||||
const SignInMethodsPreview = ({ data }: Props) => {
|
||||
const { data: groups, error } = useConnectorGroups();
|
||||
const { signInMethods, socialSignInConnectorTargets } = data;
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
|
||||
const connectorNames = useMemo(() => {
|
||||
if (!groups) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return socialSignInConnectorTargets.map((connectorTarget) => {
|
||||
const group = groups.find(({ target }) => target === connectorTarget);
|
||||
|
||||
if (!group) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<UnnamedTrans key={connectorTarget} className={styles.connector} resource={group.name} />
|
||||
);
|
||||
});
|
||||
}, [groups, socialSignInConnectorTargets]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
{!groups && !error && <div>loading</div>}
|
||||
{!groups && error && <div>{error.body?.message ?? error.message}</div>}
|
||||
{groups &&
|
||||
Object.values(SignInMethodKey)
|
||||
.filter((key) => signInMethods[key] !== SignInMethodState.Disabled)
|
||||
.map((key) => (
|
||||
<div key={key}>
|
||||
{t('sign_in_exp.sign_in_methods.methods', { context: key })}
|
||||
{key === SignInMethodKey.Social && <span>: {connectorNames}</span>}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SignInMethodsPreview;
|
|
@ -1,14 +1,6 @@
|
|||
import type { SignInExperience, SignInMethodKey, SignUp } from '@logto/schemas';
|
||||
import type { SignInExperience, SignUp } from '@logto/schemas';
|
||||
|
||||
export type SignInExperienceForm = Omit<SignInExperience, 'signInMethods' | 'signUp'> & {
|
||||
signInMethods: {
|
||||
primary?: SignInMethodKey;
|
||||
enableSecondary: boolean;
|
||||
username: boolean;
|
||||
sms: boolean;
|
||||
email: boolean;
|
||||
social: boolean;
|
||||
};
|
||||
signUp: Partial<SignUp>;
|
||||
createAccountEnabled: boolean;
|
||||
};
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import en from '@logto/phrases-ui/lib/locales/en';
|
||||
import type { SignInExperience, SignInMethods, Translation } from '@logto/schemas';
|
||||
import { SignUpIdentifier, SignInMethodKey, SignInMethodState, SignInMode } from '@logto/schemas';
|
||||
import type { SignInExperience, Translation } from '@logto/schemas';
|
||||
import { SignUpIdentifier, SignInMode } from '@logto/schemas';
|
||||
import { conditional } from '@silverhand/essentials';
|
||||
|
||||
import {
|
||||
|
@ -10,49 +10,12 @@ import {
|
|||
} from './tabs/SignUpAndSignInTab/components/SignUpAndSignInDiffSection/utilities';
|
||||
import type { SignInExperienceForm } from './types';
|
||||
|
||||
const findMethodState = (
|
||||
setup: SignInExperienceForm,
|
||||
method: keyof SignInMethods
|
||||
): SignInMethodState => {
|
||||
const { signInMethods } = setup;
|
||||
|
||||
if (signInMethods.primary === method) {
|
||||
return SignInMethodState.Primary;
|
||||
}
|
||||
|
||||
if (!signInMethods.enableSecondary) {
|
||||
return SignInMethodState.Disabled;
|
||||
}
|
||||
|
||||
if (signInMethods[method]) {
|
||||
return SignInMethodState.Secondary;
|
||||
}
|
||||
|
||||
return SignInMethodState.Disabled;
|
||||
};
|
||||
|
||||
export const signInExperienceParser = {
|
||||
toLocalForm: (signInExperience: SignInExperience): SignInExperienceForm => {
|
||||
const methodKeys = Object.values(SignInMethodKey);
|
||||
const primaryMethod = methodKeys.find(
|
||||
(key) => signInExperience.signInMethods[key] === SignInMethodState.Primary
|
||||
);
|
||||
const secondaryMethods = methodKeys.filter(
|
||||
(key) => signInExperience.signInMethods[key] === SignInMethodState.Secondary
|
||||
);
|
||||
|
||||
const { signInMode } = signInExperience;
|
||||
|
||||
return {
|
||||
...signInExperience,
|
||||
signInMethods: {
|
||||
primary: primaryMethod,
|
||||
enableSecondary: secondaryMethods.length > 0,
|
||||
username: secondaryMethods.includes(SignInMethodKey.Username),
|
||||
sms: secondaryMethods.includes(SignInMethodKey.Sms),
|
||||
email: secondaryMethods.includes(SignInMethodKey.Email),
|
||||
social: secondaryMethods.includes(SignInMethodKey.Social),
|
||||
},
|
||||
createAccountEnabled: signInMode !== SignInMode.SignIn,
|
||||
};
|
||||
},
|
||||
|
@ -67,12 +30,6 @@ export const signInExperienceParser = {
|
|||
darkLogoUrl: conditional(branding.darkLogoUrl?.length && branding.darkLogoUrl),
|
||||
slogan: conditional(branding.slogan?.length && branding.slogan),
|
||||
},
|
||||
signInMethods: {
|
||||
username: findMethodState(setup, 'username'),
|
||||
sms: findMethodState(setup, 'sms'),
|
||||
email: findMethodState(setup, 'email'),
|
||||
social: findMethodState(setup, 'social'),
|
||||
},
|
||||
signUp: {
|
||||
identifier: signUp.identifier ?? SignUpIdentifier.Username,
|
||||
password: Boolean(signUp.password),
|
||||
|
|
|
@ -2,19 +2,12 @@ import type {
|
|||
Branding,
|
||||
LanguageInfo,
|
||||
SignInExperience,
|
||||
SignInMethods,
|
||||
TermsOfUse,
|
||||
Color,
|
||||
SignUp,
|
||||
SignIn,
|
||||
} from '@logto/schemas';
|
||||
import {
|
||||
BrandingStyle,
|
||||
SignInMethodState,
|
||||
SignInMode,
|
||||
SignUpIdentifier,
|
||||
SignInIdentifier,
|
||||
} from '@logto/schemas';
|
||||
import { BrandingStyle, SignInMode, SignUpIdentifier, SignInIdentifier } from '@logto/schemas';
|
||||
|
||||
export const mockSignInExperience: SignInExperience = {
|
||||
id: 'foo',
|
||||
|
@ -62,12 +55,6 @@ export const mockSignInExperience: SignInExperience = {
|
|||
},
|
||||
],
|
||||
},
|
||||
signInMethods: {
|
||||
username: SignInMethodState.Primary,
|
||||
email: SignInMethodState.Disabled,
|
||||
sms: SignInMethodState.Disabled,
|
||||
social: SignInMethodState.Secondary,
|
||||
},
|
||||
socialSignInConnectorTargets: ['github', 'facebook', 'wechat'],
|
||||
signInMode: SignInMode.SignInAndRegister,
|
||||
};
|
||||
|
@ -94,13 +81,6 @@ export const mockLanguageInfo: LanguageInfo = {
|
|||
fallbackLanguage: 'en',
|
||||
};
|
||||
|
||||
export const mockSignInMethods: SignInMethods = {
|
||||
username: SignInMethodState.Primary,
|
||||
email: SignInMethodState.Disabled,
|
||||
sms: SignInMethodState.Disabled,
|
||||
social: SignInMethodState.Disabled,
|
||||
};
|
||||
|
||||
export const mockSignUp: SignUp = {
|
||||
identifier: SignUpIdentifier.Username,
|
||||
password: true,
|
||||
|
|
|
@ -6,7 +6,6 @@ import RequestError from '@/errors/RequestError';
|
|||
import { findAllCustomLanguageTags } from '@/queries/custom-phrase';
|
||||
import assertThat from '@/utils/assert-that';
|
||||
|
||||
export * from './sign-in-methods';
|
||||
export * from './sign-up';
|
||||
export * from './sign-in';
|
||||
|
||||
|
|
|
@ -1,107 +0,0 @@
|
|||
import { SignInMethodState, ConnectorType } from '@logto/schemas';
|
||||
|
||||
import {
|
||||
mockAliyunDmConnector,
|
||||
mockFacebookConnector,
|
||||
mockGithubConnector,
|
||||
mockSignInMethods,
|
||||
} from '@/__mocks__';
|
||||
import RequestError from '@/errors/RequestError';
|
||||
import { isEnabled, validateSignInMethods } from '@/lib/sign-in-experience';
|
||||
|
||||
const enabledConnectors = [mockFacebookConnector, mockGithubConnector];
|
||||
|
||||
describe('check whether the social sign-in method state is enabled', () => {
|
||||
it('should be truthy when sign-in method state is primary', () => {
|
||||
expect(isEnabled(SignInMethodState.Primary)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should be truthy when sign-in method state is secondary', () => {
|
||||
expect(isEnabled(SignInMethodState.Secondary)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should be falsy when sign-in method state is disabled', () => {
|
||||
expect(isEnabled(SignInMethodState.Disabled)).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('validate sign-in methods', () => {
|
||||
describe('There must be one and only one primary sign-in method.', () => {
|
||||
test('should throw when there is no primary sign-in method', () => {
|
||||
expect(() => {
|
||||
validateSignInMethods(
|
||||
{ ...mockSignInMethods, username: SignInMethodState.Disabled },
|
||||
[],
|
||||
[]
|
||||
);
|
||||
}).toMatchError(
|
||||
new RequestError('sign_in_experiences.not_one_and_only_one_primary_sign_in_method')
|
||||
);
|
||||
});
|
||||
|
||||
test('should throw when there are more than one primary sign-in methods', () => {
|
||||
expect(() => {
|
||||
validateSignInMethods({ ...mockSignInMethods, social: SignInMethodState.Primary }, [], []);
|
||||
}).toMatchError(
|
||||
new RequestError('sign_in_experiences.not_one_and_only_one_primary_sign_in_method')
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('There must be at least one enabled connector when the specific sign-in method is enabled.', () => {
|
||||
test('should throw when there is no enabled email connector and email sign-in method is enabled', async () => {
|
||||
expect(() => {
|
||||
validateSignInMethods(
|
||||
{ ...mockSignInMethods, email: SignInMethodState.Secondary },
|
||||
[],
|
||||
enabledConnectors
|
||||
);
|
||||
}).toMatchError(
|
||||
new RequestError({
|
||||
code: 'sign_in_experiences.enabled_connector_not_found',
|
||||
type: ConnectorType.Email,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
test('should throw when there is no enabled SMS connector and SMS sign-in method is enabled', () => {
|
||||
expect(() => {
|
||||
validateSignInMethods(
|
||||
{ ...mockSignInMethods, sms: SignInMethodState.Secondary },
|
||||
[],
|
||||
enabledConnectors
|
||||
);
|
||||
}).toMatchError(
|
||||
new RequestError({
|
||||
code: 'sign_in_experiences.enabled_connector_not_found',
|
||||
type: ConnectorType.Sms,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
test('should throw when there is no enabled social connector and social sign-in method is enabled', () => {
|
||||
expect(() => {
|
||||
validateSignInMethods(
|
||||
{ ...mockSignInMethods, social: SignInMethodState.Secondary },
|
||||
[],
|
||||
[mockAliyunDmConnector]
|
||||
);
|
||||
}).toMatchError(
|
||||
new RequestError({
|
||||
code: 'sign_in_experiences.enabled_connector_not_found',
|
||||
type: ConnectorType.Social,
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test('should throw when the social connector targets are empty and social sign-in method is enabled', () => {
|
||||
expect(() => {
|
||||
validateSignInMethods(
|
||||
{ ...mockSignInMethods, social: SignInMethodState.Secondary },
|
||||
[],
|
||||
enabledConnectors
|
||||
);
|
||||
}).toMatchError(new RequestError('sign_in_experiences.empty_social_connectors'));
|
||||
});
|
||||
});
|
|
@ -1,57 +0,0 @@
|
|||
import type { SignInMethods } from '@logto/schemas';
|
||||
import { SignInMethodState } from '@logto/schemas';
|
||||
import type { Optional } from '@silverhand/essentials';
|
||||
|
||||
import type { LogtoConnector } from '@/connectors/types';
|
||||
import { ConnectorType } from '@/connectors/types';
|
||||
import RequestError from '@/errors/RequestError';
|
||||
import assertThat from '@/utils/assert-that';
|
||||
|
||||
export const isEnabled = (state: SignInMethodState) => state !== SignInMethodState.Disabled;
|
||||
|
||||
export const validateSignInMethods = (
|
||||
signInMethods: SignInMethods,
|
||||
socialSignInConnectorTargets: Optional<string[]>,
|
||||
enabledConnectors: LogtoConnector[]
|
||||
) => {
|
||||
const signInMethodStates = Object.values(signInMethods);
|
||||
assertThat(
|
||||
signInMethodStates.filter((state) => state === SignInMethodState.Primary).length === 1,
|
||||
'sign_in_experiences.not_one_and_only_one_primary_sign_in_method'
|
||||
);
|
||||
|
||||
if (isEnabled(signInMethods.email)) {
|
||||
assertThat(
|
||||
enabledConnectors.some((item) => item.type === ConnectorType.Email),
|
||||
new RequestError({
|
||||
code: 'sign_in_experiences.enabled_connector_not_found',
|
||||
type: ConnectorType.Email,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (isEnabled(signInMethods.sms)) {
|
||||
assertThat(
|
||||
enabledConnectors.some((item) => item.type === ConnectorType.Sms),
|
||||
new RequestError({
|
||||
code: 'sign_in_experiences.enabled_connector_not_found',
|
||||
type: ConnectorType.Sms,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (isEnabled(signInMethods.social)) {
|
||||
assertThat(
|
||||
enabledConnectors.some((item) => item.type === ConnectorType.Social),
|
||||
new RequestError({
|
||||
code: 'sign_in_experiences.enabled_connector_not_found',
|
||||
type: ConnectorType.Social,
|
||||
})
|
||||
);
|
||||
|
||||
assertThat(
|
||||
socialSignInConnectorTargets && socialSignInConnectorTargets.length > 0,
|
||||
'sign_in_experiences.empty_social_connectors'
|
||||
);
|
||||
}
|
||||
};
|
|
@ -28,14 +28,13 @@ describe('sign-in-experience query', () => {
|
|||
languageInfo: JSON.stringify(mockSignInExperience.languageInfo),
|
||||
signIn: JSON.stringify(mockSignInExperience.signIn),
|
||||
signUp: JSON.stringify(mockSignInExperience.signUp),
|
||||
signInMethods: JSON.stringify(mockSignInExperience.socialSignInConnectorTargets),
|
||||
socialSignInConnectorTargets: JSON.stringify(mockSignInExperience.socialSignInConnectorTargets),
|
||||
};
|
||||
|
||||
it('findDefaultSignInExperience', async () => {
|
||||
/* eslint-disable sql/no-unsafe-query */
|
||||
const expectSql = `
|
||||
select "id", "color", "branding", "language_info", "terms_of_use", "sign_in_methods", "sign_in", "sign_up", "social_sign_in_connector_targets", "sign_in_mode"
|
||||
select "id", "color", "branding", "language_info", "terms_of_use", "sign_in", "sign_up", "social_sign_in_connector_targets", "sign_in_mode"
|
||||
from "sign_in_experiences"
|
||||
where "id"=$1
|
||||
`;
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import type { CreateSignInExperience, LanguageInfo, SignInExperience } from '@logto/schemas';
|
||||
import { SignInMethodState } from '@logto/schemas';
|
||||
|
||||
import {
|
||||
mockAliyunDmConnector,
|
||||
|
@ -9,7 +8,6 @@ import {
|
|||
mockGoogleConnector,
|
||||
mockLanguageInfo,
|
||||
mockSignInExperience,
|
||||
mockSignInMethods,
|
||||
mockTermsOfUse,
|
||||
} from '@/__mocks__';
|
||||
import { createRequester } from '@/utils/test-utils';
|
||||
|
@ -138,150 +136,12 @@ describe('languageInfo', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('signInMethods', () => {
|
||||
const validSignInMethodStates = Object.values(SignInMethodState);
|
||||
const invalidSignInMethodStates = [undefined, null, '', ' \t\n\r', 'invalid'];
|
||||
|
||||
describe('username', () => {
|
||||
test.each(validSignInMethodStates)('%p should success', async (state) => {
|
||||
if (state === SignInMethodState.Primary) {
|
||||
return;
|
||||
}
|
||||
const signInExperience = {
|
||||
signInMethods: {
|
||||
username: state,
|
||||
email: SignInMethodState.Primary,
|
||||
sms: SignInMethodState.Disabled,
|
||||
social: SignInMethodState.Disabled,
|
||||
},
|
||||
};
|
||||
await expectPatchResponseStatus(signInExperience, 200);
|
||||
});
|
||||
|
||||
test.each(invalidSignInMethodStates)('%p should fail', async (state) => {
|
||||
if (state === SignInMethodState.Primary) {
|
||||
return;
|
||||
}
|
||||
const signInExperience = {
|
||||
signInMethods: {
|
||||
username: state,
|
||||
email: SignInMethodState.Primary,
|
||||
sms: SignInMethodState.Disabled,
|
||||
social: SignInMethodState.Disabled,
|
||||
},
|
||||
};
|
||||
await expectPatchResponseStatus(signInExperience, 400);
|
||||
});
|
||||
});
|
||||
|
||||
describe('email', () => {
|
||||
test.each(validSignInMethodStates)('%p should success', async (state) => {
|
||||
if (state === SignInMethodState.Primary) {
|
||||
return;
|
||||
}
|
||||
const signInExperience = {
|
||||
signInMethods: {
|
||||
username: SignInMethodState.Disabled,
|
||||
email: state,
|
||||
sms: SignInMethodState.Primary,
|
||||
social: SignInMethodState.Disabled,
|
||||
},
|
||||
};
|
||||
await expectPatchResponseStatus(signInExperience, 200);
|
||||
});
|
||||
|
||||
test.each(invalidSignInMethodStates)('%p should fail', async (state) => {
|
||||
if (state === SignInMethodState.Primary) {
|
||||
return;
|
||||
}
|
||||
const signInExperience = {
|
||||
signInMethods: {
|
||||
username: SignInMethodState.Disabled,
|
||||
email: state,
|
||||
sms: SignInMethodState.Primary,
|
||||
social: SignInMethodState.Disabled,
|
||||
},
|
||||
};
|
||||
await expectPatchResponseStatus(signInExperience, 400);
|
||||
});
|
||||
});
|
||||
|
||||
describe('sms', () => {
|
||||
test.each(validSignInMethodStates)('%p should success', async (state) => {
|
||||
if (state === SignInMethodState.Primary) {
|
||||
return;
|
||||
}
|
||||
const signInExperience = {
|
||||
signInMethods: {
|
||||
username: SignInMethodState.Disabled,
|
||||
email: SignInMethodState.Disabled,
|
||||
sms: state,
|
||||
social: SignInMethodState.Primary,
|
||||
},
|
||||
socialSignInConnectorTargets: ['github'],
|
||||
};
|
||||
await expectPatchResponseStatus(signInExperience, 200);
|
||||
});
|
||||
|
||||
test.each(invalidSignInMethodStates)('%p should fail', async (state) => {
|
||||
if (state === SignInMethodState.Primary) {
|
||||
return;
|
||||
}
|
||||
const signInExperience = {
|
||||
signInMethods: {
|
||||
username: SignInMethodState.Disabled,
|
||||
email: SignInMethodState.Disabled,
|
||||
sms: state,
|
||||
social: SignInMethodState.Primary,
|
||||
},
|
||||
socialSignInConnectorTargets: ['github'],
|
||||
};
|
||||
await expectPatchResponseStatus(signInExperience, 400);
|
||||
});
|
||||
});
|
||||
|
||||
describe('social', () => {
|
||||
test.each(validSignInMethodStates)('%p should success', async (state) => {
|
||||
if (state === SignInMethodState.Primary) {
|
||||
return;
|
||||
}
|
||||
const signInExperience = {
|
||||
signInMethods: {
|
||||
username: SignInMethodState.Primary,
|
||||
email: SignInMethodState.Disabled,
|
||||
sms: SignInMethodState.Disabled,
|
||||
social: state,
|
||||
},
|
||||
socialSignInConnectorTargets: ['github'],
|
||||
};
|
||||
await expectPatchResponseStatus(signInExperience, 200);
|
||||
});
|
||||
|
||||
test.each(invalidSignInMethodStates)('%p should fail', async (state) => {
|
||||
if (state === SignInMethodState.Primary) {
|
||||
return;
|
||||
}
|
||||
const signInExperience = {
|
||||
signInMethods: {
|
||||
username: SignInMethodState.Primary,
|
||||
email: SignInMethodState.Disabled,
|
||||
sms: SignInMethodState.Disabled,
|
||||
social: state,
|
||||
},
|
||||
socialSignInConnectorTargets: ['github'],
|
||||
};
|
||||
await expectPatchResponseStatus(signInExperience, 400);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('socialSignInConnectorTargets', () => {
|
||||
test.each([[['facebook']], [['facebook', 'github']]])(
|
||||
'%p should success',
|
||||
async (socialSignInConnectorTargets) => {
|
||||
await expectPatchResponseStatus(
|
||||
{
|
||||
signInMethods: { ...mockSignInMethods, social: SignInMethodState.Secondary },
|
||||
socialSignInConnectorTargets,
|
||||
},
|
||||
200
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import type { SignInExperience, CreateSignInExperience, TermsOfUse } from '@logto/schemas';
|
||||
import { SignInMethodState } from '@logto/schemas';
|
||||
|
||||
import {
|
||||
mockFacebookConnector,
|
||||
|
@ -7,7 +6,6 @@ import {
|
|||
mockGoogleConnector,
|
||||
mockBranding,
|
||||
mockSignInExperience,
|
||||
mockSignInMethods,
|
||||
mockWechatConnector,
|
||||
mockColor,
|
||||
mockSignUp,
|
||||
|
@ -17,7 +15,6 @@ import {
|
|||
} from '@/__mocks__';
|
||||
import * as signInExpLib from '@/lib/sign-in-experience';
|
||||
import * as signInLib from '@/lib/sign-in-experience/sign-in';
|
||||
import * as signInMethodsLib from '@/lib/sign-in-experience/sign-in-methods';
|
||||
import * as signUpLib from '@/lib/sign-in-experience/sign-up';
|
||||
import { createRequester } from '@/utils/test-utils';
|
||||
|
||||
|
@ -73,10 +70,8 @@ describe('GET /sign-in-exp', () => {
|
|||
|
||||
describe('PATCH /sign-in-exp', () => {
|
||||
it('should update social connector targets in correct sorting order', async () => {
|
||||
const signInMethods = { ...mockSignInMethods, social: SignInMethodState.Secondary };
|
||||
const socialSignInConnectorTargets = ['github', 'facebook'];
|
||||
const signInExperience = {
|
||||
signInMethods,
|
||||
socialSignInConnectorTargets,
|
||||
};
|
||||
const response = await signInExperienceRequester.patch('/sign-in-exp').send(signInExperience);
|
||||
|
@ -84,17 +79,14 @@ describe('PATCH /sign-in-exp', () => {
|
|||
status: 200,
|
||||
body: {
|
||||
...mockSignInExperience,
|
||||
signInMethods,
|
||||
socialSignInConnectorTargets,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should filter out unavailable social connector targets', async () => {
|
||||
const signInMethods = { ...mockSignInMethods, social: SignInMethodState.Secondary };
|
||||
const socialSignInConnectorTargets = ['github', 'facebook', 'google'];
|
||||
const signInExperience = {
|
||||
signInMethods,
|
||||
socialSignInConnectorTargets,
|
||||
};
|
||||
const response = await signInExperienceRequester.patch('/sign-in-exp').send(signInExperience);
|
||||
|
@ -102,7 +94,6 @@ describe('PATCH /sign-in-exp', () => {
|
|||
status: 200,
|
||||
body: {
|
||||
...mockSignInExperience,
|
||||
signInMethods,
|
||||
socialSignInConnectorTargets: ['github', 'facebook'],
|
||||
},
|
||||
});
|
||||
|
@ -115,7 +106,6 @@ describe('PATCH /sign-in-exp', () => {
|
|||
const validateBranding = jest.spyOn(signInExpLib, 'validateBranding');
|
||||
const validateLanguageInfo = jest.spyOn(signInExpLib, 'validateLanguageInfo');
|
||||
const validateTermsOfUse = jest.spyOn(signInExpLib, 'validateTermsOfUse');
|
||||
const validateSignInMethods = jest.spyOn(signInMethodsLib, 'validateSignInMethods');
|
||||
const validateSignIn = jest.spyOn(signInLib, 'validateSignIn');
|
||||
const validateSignUp = jest.spyOn(signUpLib, 'validateSignUp');
|
||||
|
||||
|
@ -124,7 +114,6 @@ describe('PATCH /sign-in-exp', () => {
|
|||
branding: mockBranding,
|
||||
languageInfo: mockLanguageInfo,
|
||||
termsOfUse,
|
||||
signInMethods: mockSignInMethods,
|
||||
socialSignInConnectorTargets,
|
||||
signUp: mockSignUp,
|
||||
signIn: mockSignIn,
|
||||
|
@ -139,11 +128,6 @@ describe('PATCH /sign-in-exp', () => {
|
|||
expect(validateBranding).toHaveBeenCalledWith(mockBranding);
|
||||
expect(validateLanguageInfo).toHaveBeenCalledWith(mockLanguageInfo);
|
||||
expect(validateTermsOfUse).toHaveBeenCalledWith(termsOfUse);
|
||||
expect(validateSignInMethods).toHaveBeenCalledWith(
|
||||
mockSignInMethods,
|
||||
socialSignInConnectorTargets,
|
||||
connectors
|
||||
);
|
||||
expect(validateSignUp).toHaveBeenCalledWith(mockSignUp, connectors);
|
||||
expect(validateSignIn).toHaveBeenCalledWith(mockSignIn, mockSignUp, connectors);
|
||||
|
||||
|
@ -154,7 +138,6 @@ describe('PATCH /sign-in-exp', () => {
|
|||
color: mockColor,
|
||||
branding: mockBranding,
|
||||
termsOfUse,
|
||||
signInMethods: mockSignInMethods,
|
||||
socialSignInConnectorTargets,
|
||||
signIn: mockSignIn,
|
||||
},
|
||||
|
|
|
@ -5,8 +5,6 @@ import {
|
|||
validateBranding,
|
||||
validateLanguageInfo,
|
||||
validateTermsOfUse,
|
||||
validateSignInMethods,
|
||||
isEnabled,
|
||||
validateSignUp,
|
||||
validateSignIn,
|
||||
} from '@/lib/sign-in-experience';
|
||||
|
@ -37,7 +35,7 @@ export default function signInExperiencesRoutes<T extends AuthedRouter>(router:
|
|||
/* eslint-disable complexity */
|
||||
async (ctx, next) => {
|
||||
const { socialSignInConnectorTargets, ...rest } = ctx.guard.body;
|
||||
const { branding, languageInfo, termsOfUse, signInMethods, signUp, signIn } = rest;
|
||||
const { branding, languageInfo, termsOfUse, signUp, signIn } = rest;
|
||||
|
||||
if (branding) {
|
||||
validateBranding(branding);
|
||||
|
@ -62,14 +60,6 @@ export default function signInExperiencesRoutes<T extends AuthedRouter>(router:
|
|||
)
|
||||
);
|
||||
|
||||
if (signInMethods) {
|
||||
validateSignInMethods(
|
||||
signInMethods,
|
||||
filteredSocialSignInConnectorTargets,
|
||||
enabledConnectors
|
||||
);
|
||||
}
|
||||
|
||||
if (signUp) {
|
||||
validateSignUp(signUp, enabledConnectors);
|
||||
}
|
||||
|
@ -80,17 +70,14 @@ export default function signInExperiencesRoutes<T extends AuthedRouter>(router:
|
|||
const signInExperience = await findDefaultSignInExperience();
|
||||
validateSignIn(signIn, signInExperience.signUp, enabledConnectors);
|
||||
}
|
||||
|
||||
// Update socialSignInConnectorTargets only when social sign-in is enabled.
|
||||
const signInExperience =
|
||||
signInMethods && isEnabled(signInMethods.social)
|
||||
ctx.body = await updateDefaultSignInExperience(
|
||||
filteredSocialSignInConnectorTargets
|
||||
? {
|
||||
...ctx.guard.body,
|
||||
...rest,
|
||||
socialSignInConnectorTargets: filteredSocialSignInConnectorTargets,
|
||||
}
|
||||
: rest;
|
||||
|
||||
ctx.body = await updateDefaultSignInExperience(signInExperience);
|
||||
: rest
|
||||
);
|
||||
|
||||
return next();
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
|
||||
import type { User, SignUpIdentifier } from '@logto/schemas';
|
||||
import type { User, SignUpIdentifier, SignIn } from '@logto/schemas';
|
||||
import { assert } from '@silverhand/essentials';
|
||||
import { HTTPError } from 'got';
|
||||
|
||||
|
@ -80,6 +80,14 @@ export const setSignUpIdentifier = async (
|
|||
await updateSignInExperience({ signUp: { identifier, password, verify } });
|
||||
};
|
||||
|
||||
export const setSignInMethod = async (methods: SignIn['methods']) => {
|
||||
await updateSignInExperience({
|
||||
signIn: {
|
||||
methods,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
type PasscodeRecord = {
|
||||
phone?: string;
|
||||
address?: string;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { SignUpIdentifier } from '@logto/schemas';
|
||||
import { SignInIdentifier, SignUpIdentifier } from '@logto/schemas';
|
||||
import { adminConsoleApplicationId } from '@logto/schemas/lib/seeds';
|
||||
import { assert } from '@silverhand/essentials';
|
||||
|
||||
|
@ -28,6 +28,7 @@ import {
|
|||
readPasscode,
|
||||
createUserByAdmin,
|
||||
setSignUpIdentifier,
|
||||
setSignInMethod,
|
||||
} from '@/helpers';
|
||||
import { generateUsername, generatePassword, generateEmail, generatePhone } from '@/utils';
|
||||
|
||||
|
@ -48,6 +49,20 @@ describe('email passwordless flow', () => {
|
|||
beforeAll(async () => {
|
||||
await setUpConnector(mockEmailConnectorId, mockEmailConnectorConfig);
|
||||
await setSignUpIdentifier(SignUpIdentifier.Email, false);
|
||||
await setSignInMethod([
|
||||
{
|
||||
identifier: SignInIdentifier.Username,
|
||||
password: true,
|
||||
verificationCode: false,
|
||||
isPasswordPrimary: true,
|
||||
},
|
||||
{
|
||||
identifier: SignInIdentifier.Email,
|
||||
password: false,
|
||||
verificationCode: true,
|
||||
isPasswordPrimary: false,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
// Since we can not create a email register user throw admin. Have to run the register then sign-in concurrently.
|
||||
|
@ -122,6 +137,20 @@ describe('sms passwordless flow', () => {
|
|||
beforeAll(async () => {
|
||||
await setUpConnector(mockSmsConnectorId, mockSmsConnectorConfig);
|
||||
await setSignUpIdentifier(SignUpIdentifier.Sms, false);
|
||||
await setSignInMethod([
|
||||
{
|
||||
identifier: SignInIdentifier.Username,
|
||||
password: true,
|
||||
verificationCode: false,
|
||||
isPasswordPrimary: true,
|
||||
},
|
||||
{
|
||||
identifier: SignInIdentifier.Sms,
|
||||
password: false,
|
||||
verificationCode: true,
|
||||
isPasswordPrimary: false,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
// Since we can not create a sms register user throw admin. Have to run the register then sign-in concurrently.
|
||||
|
|
|
@ -1,16 +1,6 @@
|
|||
import { BrandingStyle, SignInMethodState } from '@logto/schemas';
|
||||
import { BrandingStyle } from '@logto/schemas';
|
||||
|
||||
import {
|
||||
mockEmailConnectorConfig,
|
||||
mockEmailConnectorId,
|
||||
mockSmsConnectorConfig,
|
||||
mockSmsConnectorId,
|
||||
mockSocialConnectorConfig,
|
||||
mockSocialConnectorId,
|
||||
mockSocialConnectorTarget,
|
||||
} from '@/__mocks__/connectors-mock';
|
||||
import { getSignInExperience, updateSignInExperience } from '@/api';
|
||||
import { updateConnectorConfig, enableConnector, disableConnector } from '@/api/connector';
|
||||
|
||||
describe('admin console sign-in experience', () => {
|
||||
it('should get sign-in experience successfully', async () => {
|
||||
|
@ -41,41 +31,4 @@ describe('admin console sign-in experience', () => {
|
|||
const updatedSignInExperience = await updateSignInExperience(newSignInExperience);
|
||||
expect(updatedSignInExperience).toMatchObject(newSignInExperience);
|
||||
});
|
||||
|
||||
it('should be able to setup sign in methods after connectors are enabled', async () => {
|
||||
// Setup connectors for tests
|
||||
await Promise.all([
|
||||
updateConnectorConfig(mockSocialConnectorId, mockSocialConnectorConfig).then(async () =>
|
||||
enableConnector(mockSocialConnectorId)
|
||||
),
|
||||
updateConnectorConfig(mockSmsConnectorId, mockSmsConnectorConfig).then(async () =>
|
||||
enableConnector(mockSmsConnectorId)
|
||||
),
|
||||
updateConnectorConfig(mockEmailConnectorId, mockEmailConnectorConfig).then(async () =>
|
||||
enableConnector(mockEmailConnectorId)
|
||||
),
|
||||
]);
|
||||
|
||||
// Set up sign-in methods
|
||||
const newSignInMethods = {
|
||||
username: SignInMethodState.Primary,
|
||||
sms: SignInMethodState.Secondary,
|
||||
email: SignInMethodState.Secondary,
|
||||
social: SignInMethodState.Secondary,
|
||||
};
|
||||
|
||||
const updatedSignInExperience = await updateSignInExperience({
|
||||
socialSignInConnectorTargets: [mockSocialConnectorTarget],
|
||||
signInMethods: newSignInMethods,
|
||||
});
|
||||
|
||||
expect(updatedSignInExperience.signInMethods).toMatchObject(newSignInMethods);
|
||||
|
||||
// Reset connectors
|
||||
await Promise.all([
|
||||
disableConnector(mockSocialConnectorId),
|
||||
disableConnector(mockSmsConnectorId),
|
||||
disableConnector(mockEmailConnectorId),
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -15,11 +15,20 @@ describe('wellknown api', () => {
|
|||
const response = await getWellKnownSignInExperience(client.interactionCookie);
|
||||
|
||||
expect(response).toMatchObject({
|
||||
signInMethods: {
|
||||
username: 'primary',
|
||||
email: 'disabled',
|
||||
sms: 'disabled',
|
||||
social: 'disabled',
|
||||
signUp: {
|
||||
identifier: 'username',
|
||||
password: true,
|
||||
verify: false,
|
||||
},
|
||||
signIn: {
|
||||
methods: [
|
||||
{
|
||||
identifier: 'username',
|
||||
password: true,
|
||||
verificationCode: false,
|
||||
isPasswordPrimary: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
signInMode: 'SignIn',
|
||||
});
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
import { sql } from 'slonik';
|
||||
|
||||
import type { AlterationScript } from '../lib/types/alteration';
|
||||
|
||||
const alteration: AlterationScript = {
|
||||
up: async (pool) => {
|
||||
await pool.query(sql`
|
||||
alter table sign_in_experiences drop column sign_in_methods
|
||||
`);
|
||||
},
|
||||
down: async (pool) => {
|
||||
await pool.query(sql`
|
||||
alter table sign_in_experiences add column sign_in_methods jsonb not null default '{}'::jsonb,
|
||||
`);
|
||||
},
|
||||
};
|
||||
|
||||
export default alteration;
|
|
@ -128,28 +128,6 @@ export const languageInfoGuard = z.object({
|
|||
|
||||
export type LanguageInfo = z.infer<typeof languageInfoGuard>;
|
||||
|
||||
export enum SignInMethodKey {
|
||||
Username = 'username',
|
||||
Email = 'email',
|
||||
Sms = 'sms',
|
||||
Social = 'social',
|
||||
}
|
||||
|
||||
export enum SignInMethodState {
|
||||
Primary = 'primary',
|
||||
Secondary = 'secondary',
|
||||
Disabled = 'disabled',
|
||||
}
|
||||
|
||||
export const signInMethodsGuard = z.object({
|
||||
[SignInMethodKey.Username]: z.nativeEnum(SignInMethodState),
|
||||
[SignInMethodKey.Email]: z.nativeEnum(SignInMethodState),
|
||||
[SignInMethodKey.Sms]: z.nativeEnum(SignInMethodState),
|
||||
[SignInMethodKey.Social]: z.nativeEnum(SignInMethodState),
|
||||
});
|
||||
|
||||
export type SignInMethods = z.infer<typeof signInMethodsGuard>;
|
||||
|
||||
export enum SignUpIdentifier {
|
||||
Email = 'email',
|
||||
Sms = 'sms',
|
||||
|
|
|
@ -2,12 +2,7 @@ import { generateDarkColor } from '@logto/core-kit';
|
|||
|
||||
import type { CreateSignInExperience } from '../db-entries';
|
||||
import { SignInMode } from '../db-entries';
|
||||
import {
|
||||
BrandingStyle,
|
||||
SignInIdentifier,
|
||||
SignInMethodState,
|
||||
SignUpIdentifier,
|
||||
} from '../foundations';
|
||||
import { BrandingStyle, SignInIdentifier, SignUpIdentifier } from '../foundations';
|
||||
|
||||
const defaultPrimaryColor = '#6139F6';
|
||||
|
||||
|
@ -43,26 +38,8 @@ export const defaultSignInExperience: Readonly<CreateSignInExperience> = {
|
|||
verificationCode: false,
|
||||
isPasswordPrimary: true,
|
||||
},
|
||||
{
|
||||
identifier: SignInIdentifier.Email,
|
||||
password: true,
|
||||
verificationCode: true,
|
||||
isPasswordPrimary: false,
|
||||
},
|
||||
{
|
||||
identifier: SignInIdentifier.Sms,
|
||||
password: true,
|
||||
verificationCode: true,
|
||||
isPasswordPrimary: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
signInMethods: {
|
||||
username: SignInMethodState.Primary,
|
||||
email: SignInMethodState.Disabled,
|
||||
sms: SignInMethodState.Disabled,
|
||||
social: SignInMethodState.Disabled,
|
||||
},
|
||||
socialSignInConnectorTargets: [],
|
||||
signInMode: SignInMode.SignInAndRegister,
|
||||
};
|
||||
|
|
|
@ -6,7 +6,6 @@ create table sign_in_experiences (
|
|||
branding jsonb /* @use Branding */ not null,
|
||||
language_info jsonb /* @use LanguageInfo */ not null,
|
||||
terms_of_use jsonb /* @use TermsOfUse */ not null,
|
||||
sign_in_methods jsonb /* @use SignInMethods */ not null,
|
||||
sign_in jsonb /* @use SignIn */ not null,
|
||||
sign_up jsonb /* @use SignUp */ not null,
|
||||
social_sign_in_connector_targets jsonb /* @use ConnectorTargets */ not null default '[]'::jsonb,
|
||||
|
|
|
@ -4,7 +4,6 @@ import {
|
|||
ConnectorPlatform,
|
||||
ConnectorType,
|
||||
SignInIdentifier,
|
||||
SignInMethodState,
|
||||
SignInMode,
|
||||
SignUpIdentifier,
|
||||
} from '@logto/schemas';
|
||||
|
@ -210,12 +209,6 @@ export const mockSignInExperience: SignInExperience = {
|
|||
signIn: {
|
||||
methods: [usernameSignInMethod, emailSignInMethod, smsSignInMethod],
|
||||
},
|
||||
signInMethods: {
|
||||
username: SignInMethodState.Primary,
|
||||
email: SignInMethodState.Secondary,
|
||||
sms: SignInMethodState.Secondary,
|
||||
social: SignInMethodState.Secondary,
|
||||
},
|
||||
socialSignInConnectorTargets: ['BE8QXN0VsrOH7xdWFDJZ9', 'lcXT4o2GSjbV9kg2shZC7'],
|
||||
signInMode: SignInMode.SignInAndRegister,
|
||||
};
|
||||
|
|
Loading…
Add table
Reference in a new issue