0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-16 20:26:19 -05:00

refactor(core): throw registered social email or phone info (#3199)

This commit is contained in:
simeng-li 2023-02-24 16:05:06 +08:00 committed by GitHub
parent 86b796e989
commit c06e2d0985
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 53 additions and 6 deletions

View file

@ -125,7 +125,7 @@ describe('validateMandatoryUserProfile', () => {
).resolves.not.toThrow(); ).resolves.not.toThrow();
}); });
it('identifier includes social with verified email but email occupied should throw', async () => { it('sign-in identifier includes social with verified email but email occupied should throw', async () => {
hasUserWithEmail.mockResolvedValueOnce(true); hasUserWithEmail.mockResolvedValueOnce(true);
await expect( await expect(
@ -144,6 +144,26 @@ describe('validateMandatoryUserProfile', () => {
); );
}); });
it('register identifier includes social with verified email but email occupied should throw', async () => {
hasUserWithEmail.mockResolvedValueOnce(true);
await expect(
validateMandatoryUserProfile(users, emailRequiredCtx, {
...interaction,
event: InteractionEvent.Register,
identifiers: [
...interaction.identifiers,
{ key: 'social', userInfo: { email: 'email', id: 'foo' }, connectorId: 'logto' },
],
})
).rejects.toMatchError(
new RequestError(
{ code: 'user.missing_profile', status: 422 },
{ missingProfile: [MissingProfile.email] }
)
);
});
it('identifier includes social with verified email should not throw', async () => { it('identifier includes social with verified email should not throw', async () => {
hasUserWithEmail.mockResolvedValueOnce(false); hasUserWithEmail.mockResolvedValueOnce(false);
@ -200,7 +220,7 @@ describe('validateMandatoryUserProfile', () => {
).resolves.not.toThrow(); ).resolves.not.toThrow();
}); });
it('identifier includes social with verified phone but phone occupied should throw', async () => { it('sign-in identifier includes social with verified phone but phone occupied should throw', async () => {
hasUserWithPhone.mockResolvedValueOnce(true); hasUserWithPhone.mockResolvedValueOnce(true);
await expect( await expect(
@ -219,6 +239,26 @@ describe('validateMandatoryUserProfile', () => {
); );
}); });
it('register identifier includes social with verified phone but phone occupied should throw', async () => {
hasUserWithPhone.mockResolvedValueOnce(true);
await expect(
validateMandatoryUserProfile(users, phoneRequiredCtx, {
...interaction,
event: InteractionEvent.Register,
identifiers: [
...interaction.identifiers,
{ key: 'social', userInfo: { phone: '123456', id: 'foo' }, connectorId: 'logto' },
],
})
).rejects.toMatchError(
new RequestError(
{ code: 'user.missing_profile', status: 422 },
{ missingProfile: [MissingProfile.phone] }
)
);
});
it('identifier includes social with verified phone should not throw', async () => { it('identifier includes social with verified phone should not throw', async () => {
hasUserWithPhone.mockResolvedValueOnce(false); hasUserWithPhone.mockResolvedValueOnce(false);

View file

@ -1,6 +1,7 @@
import type { Profile, SignInExperience, User } from '@logto/schemas'; import type { Profile, SignInExperience, User } from '@logto/schemas';
import { InteractionEvent, MissingProfile, SignInIdentifier } from '@logto/schemas'; import { InteractionEvent, MissingProfile, SignInIdentifier } from '@logto/schemas';
import type { Nullable } from '@silverhand/essentials'; import type { Nullable } from '@silverhand/essentials';
import { conditional } from '@silverhand/essentials';
import type { Context } from 'koa'; import type { Context } from 'koa';
import RequestError from '#src/errors/RequestError/index.js'; import RequestError from '#src/errors/RequestError/index.js';
@ -119,7 +120,7 @@ const fillMissingProfileWithSocialIdentity = async (
interaction: MandatoryProfileValidationInteraction, interaction: MandatoryProfileValidationInteraction,
userQueries: Queries['users'] userQueries: Queries['users']
): Promise<MandatoryProfileValidationInteraction> => { ): Promise<MandatoryProfileValidationInteraction> => {
const { identifiers, profile } = interaction; const { identifiers, profile, event } = interaction;
const socialUserInfo = getSocialUserInfo(identifiers); const socialUserInfo = getSocialUserInfo(identifiers);
@ -144,7 +145,10 @@ const fillMissingProfileWithSocialIdentity = async (
{ code: 'user.missing_profile', status: 422 }, { code: 'user.missing_profile', status: 422 },
{ {
missingProfile: Array.from(missingProfileSet), missingProfile: Array.from(missingProfileSet),
registeredSocialIdentity: { email }, // Throw taken email when it's sign-in event
...conditional(
event === InteractionEvent.SignIn && { registeredSocialIdentity: { email } }
),
} }
) )
); );
@ -178,7 +182,10 @@ const fillMissingProfileWithSocialIdentity = async (
{ code: 'user.missing_profile', status: 422 }, { code: 'user.missing_profile', status: 422 },
{ {
missingProfile: Array.from(missingProfileSet), missingProfile: Array.from(missingProfileSet),
registeredSocialIdentity: { phone }, // Throw taken phone when it's sign-in event
...conditional(
event === InteractionEvent.SignIn && { registeredSocialIdentity: { phone } }
),
} }
) )
); );

View file

@ -27,7 +27,7 @@ const useRequiredProfileErrorHandler = ({ replace, linkSocial }: Options = {}) =
// Required as a sign up method but missing in the user profile // Required as a sign up method but missing in the user profile
const missingProfile = data?.missingProfile[0]; const missingProfile = data?.missingProfile[0];
// Required as a sign up method can be found in Social Identity (email / phone), but registered with a different account // Required as a sign up method, verified email or phone can be found in Social Identity, but registered with a different account
const registeredSocialIdentity = data?.registeredSocialIdentity; const registeredSocialIdentity = data?.registeredSocialIdentity;
const linkSocialQueryString = linkSocial const linkSocialQueryString = linkSocial