Skip to content

Commit c7a575b

Browse files
committed
feat(core,schemas): add secondaryIdentifiers to SIE signUp settings
add secondaryIdentifiers to SIE signUp settings
1 parent 53d2bab commit c7a575b

File tree

8 files changed

+317
-187
lines changed

8 files changed

+317
-187
lines changed

packages/core/src/libraries/sign-in-experience/sign-in.test.ts

Lines changed: 0 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -113,104 +113,6 @@ describe('validate sign-in', () => {
113113
});
114114
});
115115

116-
describe('The sign up identifier must be included in sign in', () => {
117-
it('throws when sign up is username and sign in methods does not include username', () => {
118-
expect(() => {
119-
validateSignIn(
120-
{
121-
methods: [
122-
{
123-
...mockSignInMethod,
124-
identifier: SignInIdentifier.Phone,
125-
},
126-
],
127-
},
128-
{
129-
...mockSignUp,
130-
identifiers: [SignInIdentifier.Username],
131-
},
132-
enabledConnectors
133-
);
134-
}).toMatchError(
135-
new RequestError({
136-
code: 'sign_in_experiences.miss_sign_up_identifier_in_sign_in',
137-
})
138-
);
139-
});
140-
141-
it('throws when sign up is email and sign in methods does not include email', () => {
142-
expect(() => {
143-
validateSignIn(
144-
{
145-
methods: [
146-
{
147-
...mockSignInMethod,
148-
identifier: SignInIdentifier.Username,
149-
},
150-
],
151-
},
152-
{
153-
...mockSignUp,
154-
identifiers: [SignInIdentifier.Email],
155-
},
156-
enabledConnectors
157-
);
158-
}).toMatchError(
159-
new RequestError({
160-
code: 'sign_in_experiences.miss_sign_up_identifier_in_sign_in',
161-
})
162-
);
163-
});
164-
165-
it('throws when sign up is phone and sign in methods does not include phone', () => {
166-
expect(() => {
167-
validateSignIn(
168-
{
169-
methods: [
170-
{
171-
...mockSignInMethod,
172-
identifier: SignInIdentifier.Username,
173-
},
174-
],
175-
},
176-
{
177-
...mockSignUp,
178-
identifiers: [SignInIdentifier.Phone],
179-
},
180-
enabledConnectors
181-
);
182-
}).toMatchError(
183-
new RequestError({
184-
code: 'sign_in_experiences.miss_sign_up_identifier_in_sign_in',
185-
})
186-
);
187-
});
188-
189-
it('throws when sign up is `email or phone` and sign in methods does not include email and phone', () => {
190-
expect(() => {
191-
validateSignIn(
192-
{
193-
methods: [
194-
{
195-
...mockSignInMethod,
196-
identifier: SignInIdentifier.Email,
197-
},
198-
],
199-
},
200-
{
201-
...mockSignUp,
202-
identifiers: [SignInIdentifier.Email, SignInIdentifier.Phone],
203-
},
204-
enabledConnectors
205-
);
206-
}).toMatchError(
207-
new RequestError({
208-
code: 'sign_in_experiences.miss_sign_up_identifier_in_sign_in',
209-
})
210-
);
211-
});
212-
});
213-
214116
it('throws when sign up requires set a password and sign in password is not enabled', () => {
215117
expect(() => {
216118
validateSignIn(

packages/core/src/libraries/sign-in-experience/sign-in.ts

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -47,35 +47,6 @@ export const validateSignIn = (
4747
})
4848
);
4949

50-
for (const identifier of signUp.identifiers) {
51-
if (identifier === SignInIdentifier.Username) {
52-
assertThat(
53-
signIn.methods.some(({ identifier }) => identifier === SignInIdentifier.Username),
54-
new RequestError({
55-
code: 'sign_in_experiences.miss_sign_up_identifier_in_sign_in',
56-
})
57-
);
58-
}
59-
60-
if (identifier === SignInIdentifier.Email) {
61-
assertThat(
62-
signIn.methods.some(({ identifier }) => identifier === SignInIdentifier.Email),
63-
new RequestError({
64-
code: 'sign_in_experiences.miss_sign_up_identifier_in_sign_in',
65-
})
66-
);
67-
}
68-
69-
if (identifier === SignInIdentifier.Phone) {
70-
assertThat(
71-
signIn.methods.some(({ identifier }) => identifier === SignInIdentifier.Phone),
72-
new RequestError({
73-
code: 'sign_in_experiences.miss_sign_up_identifier_in_sign_in',
74-
})
75-
);
76-
}
77-
}
78-
7950
if (signUp.password) {
8051
assertThat(
8152
signIn.methods.every(({ password }) => password),

packages/core/src/libraries/sign-in-experience/sign-up.test.ts

Lines changed: 134 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,70 @@
1-
import { ConnectorType, SignInIdentifier } from '@logto/schemas';
2-
import { createMockUtils } from '@logto/shared/esm';
1+
import {
2+
AlternativeSignUpIdentifier,
3+
ConnectorType,
4+
SignInIdentifier,
5+
type SignUp,
6+
} from '@logto/schemas';
37

48
import { mockAliyunDmConnector, mockAliyunSmsConnector, mockSignUp } from '#src/__mocks__/index.js';
59
import RequestError from '#src/errors/RequestError/index.js';
610

7-
const { jest } = import.meta;
8-
const { mockEsmWithActual } = createMockUtils(jest);
9-
1011
const enabledConnectors = [mockAliyunDmConnector, mockAliyunSmsConnector];
1112

1213
const { validateSignUp } = await import('./sign-up.js');
1314

1415
describe('validate sign-up', () => {
16+
it('should throw when setting secondary identifiers without primary identifiers', async () => {
17+
expect(() => {
18+
validateSignUp(
19+
{
20+
...mockSignUp,
21+
identifiers: [],
22+
secondaryIdentifiers: [{ identifier: SignInIdentifier.Email }],
23+
},
24+
enabledConnectors
25+
);
26+
}).toMatchError(
27+
new RequestError({
28+
code: 'sign_in_experiences.missing_sign_up_identifiers',
29+
})
30+
);
31+
});
32+
33+
describe('Sign up identifiers must be unique.', () => {
34+
const errorTestCase: Array<Pick<SignUp, 'identifiers' | 'secondaryIdentifiers'>> = [
35+
{ identifiers: [SignInIdentifier.Email, SignInIdentifier.Email] },
36+
{
37+
identifiers: [SignInIdentifier.Username],
38+
secondaryIdentifiers: [{ identifier: SignInIdentifier.Username }],
39+
},
40+
{
41+
identifiers: [SignInIdentifier.Email, SignInIdentifier.Phone],
42+
secondaryIdentifiers: [{ identifier: SignInIdentifier.Phone }],
43+
},
44+
{
45+
identifiers: [SignInIdentifier.Phone],
46+
secondaryIdentifiers: [{ identifier: AlternativeSignUpIdentifier.EmailOrPhone }],
47+
},
48+
];
49+
50+
test.each(errorTestCase)(
51+
'should throw when there are duplicated sign up identifiers',
52+
async (signUp) => {
53+
expect(() => {
54+
validateSignUp({ ...mockSignUp, ...signUp }, enabledConnectors);
55+
}).toMatchError(
56+
new RequestError({
57+
code: 'sign_in_experiences.duplicated_sign_up_identifiers',
58+
})
59+
);
60+
}
61+
);
62+
});
63+
1564
describe('There must be at least one connector for the specific identifier.', () => {
1665
test('should throw when there is no email connector and identifier is email', async () => {
1766
expect(() => {
18-
validateSignUp({ ...mockSignUp, identifiers: [SignInIdentifier.Email] }, []);
67+
validateSignUp({ ...mockSignUp, identifiers: [SignInIdentifier.Email], verify: true }, []);
1968
}).toMatchError(
2069
new RequestError({
2170
code: 'sign_in_experiences.enabled_connector_not_found',
@@ -30,6 +79,7 @@ describe('validate sign-up', () => {
3079
{
3180
...mockSignUp,
3281
identifiers: [SignInIdentifier.Email, SignInIdentifier.Phone],
82+
verify: true,
3383
},
3484
[]
3585
);
@@ -43,7 +93,7 @@ describe('validate sign-up', () => {
4393

4494
test('should throw when there is no sms connector and identifier is phone', async () => {
4595
expect(() => {
46-
validateSignUp({ ...mockSignUp, identifiers: [SignInIdentifier.Phone] }, []);
96+
validateSignUp({ ...mockSignUp, identifiers: [SignInIdentifier.Phone], verify: true }, []);
4797
}).toMatchError(
4898
new RequestError({
4999
code: 'sign_in_experiences.enabled_connector_not_found',
@@ -69,19 +119,59 @@ describe('validate sign-up', () => {
69119
})
70120
);
71121
});
72-
});
73122

74-
test('should throw when identifier is username and password is false', async () => {
75-
expect(() => {
76-
validateSignUp(
77-
{ ...mockSignUp, identifiers: [SignInIdentifier.Username], password: false },
78-
enabledConnectors
123+
test('should throw when there is no email connector and secondary identifier is email', async () => {
124+
expect(() => {
125+
validateSignUp(
126+
{
127+
...mockSignUp,
128+
secondaryIdentifiers: [{ identifier: SignInIdentifier.Email, verify: true }],
129+
},
130+
[]
131+
);
132+
}).toMatchError(
133+
new RequestError({
134+
code: 'sign_in_experiences.enabled_connector_not_found',
135+
type: ConnectorType.Email,
136+
})
79137
);
80-
}).toMatchError(
81-
new RequestError({
82-
code: 'sign_in_experiences.username_requires_password',
83-
})
84-
);
138+
});
139+
140+
test('should throw when there is no sms connector and secondary identifier is phone', async () => {
141+
expect(() => {
142+
validateSignUp(
143+
{
144+
...mockSignUp,
145+
secondaryIdentifiers: [{ identifier: SignInIdentifier.Phone, verify: true }],
146+
},
147+
[]
148+
);
149+
}).toMatchError(
150+
new RequestError({
151+
code: 'sign_in_experiences.enabled_connector_not_found',
152+
type: ConnectorType.Sms,
153+
})
154+
);
155+
});
156+
157+
test('should throw when there is no email connector and secondary identifier is email or phone', async () => {
158+
expect(() => {
159+
validateSignUp(
160+
{
161+
...mockSignUp,
162+
secondaryIdentifiers: [
163+
{ identifier: AlternativeSignUpIdentifier.EmailOrPhone, verify: true },
164+
],
165+
},
166+
[]
167+
);
168+
}).toMatchError(
169+
new RequestError({
170+
code: 'sign_in_experiences.enabled_connector_not_found',
171+
type: ConnectorType.Email,
172+
})
173+
);
174+
});
85175
});
86176

87177
describe('verify should be true for passwordless identifier', () => {
@@ -127,5 +217,31 @@ describe('validate sign-up', () => {
127217
})
128218
);
129219
});
220+
221+
test.each([
222+
{
223+
identifier: SignInIdentifier.Email,
224+
},
225+
{
226+
identifier: SignInIdentifier.Phone,
227+
},
228+
{
229+
identifier: AlternativeSignUpIdentifier.EmailOrPhone,
230+
},
231+
])('should not throw when identifier is %p and verify is not true', async (identifier) => {
232+
expect(() => {
233+
validateSignUp(
234+
{
235+
...mockSignUp,
236+
secondaryIdentifiers: [identifier],
237+
},
238+
enabledConnectors
239+
);
240+
}).toMatchError(
241+
new RequestError({
242+
code: 'sign_in_experiences.passwordless_requires_verify',
243+
})
244+
);
245+
});
130246
});
131247
});

0 commit comments

Comments
 (0)