0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-02-17 22:04:19 -05:00

fix(core): handle not found error for password sign in (#2383)

This commit is contained in:
wangsijie 2022-11-11 14:54:46 +08:00 committed by GitHub
parent 4028669940
commit 27ce104ec5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 28 additions and 42 deletions

View file

@ -8,12 +8,7 @@ import { getLogtoConnectorById } from '@/connectors';
import type { SocialUserInfo } from '@/connectors/types';
import { socialUserInfoGuard } from '@/connectors/types';
import RequestError from '@/errors/RequestError';
import {
findUserByEmail,
findUserByPhone,
hasUserWithEmail,
hasUserWithPhone,
} from '@/queries/user';
import { findUserByEmail, findUserByPhone } from '@/queries/user';
import assertThat from '@/utils/assert-that';
export type SocialUserInfoSession = {
@ -88,16 +83,20 @@ export const getUserInfoFromInteractionResult = async (
export const findSocialRelatedUser = async (
info: SocialUserInfo
): Promise<Nullable<[{ type: 'email' | 'phone'; value: string }, User]>> => {
if (info.phone && (await hasUserWithPhone(info.phone))) {
if (info.phone) {
const user = await findUserByPhone(info.phone);
return [{ type: 'phone', value: info.phone }, user];
if (user) {
return [{ type: 'phone', value: info.phone }, user];
}
}
if (info.email && (await hasUserWithEmail(info.email))) {
if (info.email) {
const user = await findUserByEmail(info.email);
return [{ type: 'email', value: info.email }, user];
if (user) {
return [{ type: 'email', value: info.email }, user];
}
}
return null;

View file

@ -18,14 +18,14 @@ export const findUserByUsername = async (username: string) =>
`);
export const findUserByEmail = async (email: string) =>
envSet.pool.one<User>(sql`
envSet.pool.maybeOne<User>(sql`
select ${sql.join(Object.values(fields), sql`,`)}
from ${table}
where lower(${fields.primaryEmail})=lower(${email})
`);
export const findUserByPhone = async (phone: string) =>
envSet.pool.one<User>(sql`
envSet.pool.maybeOne<User>(sql`
select ${sql.join(Object.values(fields), sql`,`)}
from ${table}
where ${fields.primaryPhone}=${phone}

View file

@ -53,12 +53,8 @@ export const smsSignInAction = <StateT, ContextT extends WithLogContext, Respons
checkValidateExpiration(expiresAt);
assertThat(
await hasUserWithPhone(phone),
new RequestError({ code: 'user.phone_not_exists', status: 404 })
);
const user = await findUserByPhone(phone);
assertThat(user, new RequestError({ code: 'user.phone_not_exists', status: 404 }));
const { id } = user;
ctx.log(type, { userId: id });
@ -99,12 +95,8 @@ export const emailSignInAction = <StateT, ContextT extends WithLogContext, Respo
checkValidateExpiration(expiresAt);
assertThat(
await hasUserWithEmail(email),
new RequestError({ code: 'user.email_not_exists', status: 404 })
);
const user = await findUserByEmail(email);
assertThat(user, new RequestError({ code: 'user.email_not_exists', status: 404 }));
const { id } = user;
ctx.log(type, { userId: id });

View file

@ -1,6 +1,7 @@
/* eslint-disable max-lines */
import type { User } from '@logto/schemas';
import { PasscodeType, SignInIdentifier, SignUpIdentifier } from '@logto/schemas';
import type { Nullable } from '@silverhand/essentials';
import { addDays, addSeconds, subDays } from 'date-fns';
import { Provider } from 'oidc-provider';
@ -14,7 +15,8 @@ import passwordlessRoutes, { registerRoute, signInRoute } from './passwordless';
const insertUser = jest.fn(async (..._args: unknown[]) => mockUser);
const findUserById = jest.fn(async (): Promise<User> => mockUser);
const findUserByEmail = jest.fn(async (): Promise<User> => mockUser);
const findUserByEmail = jest.fn(async (): Promise<Nullable<User>> => mockUser);
const findUserByPhone = jest.fn(async (): Promise<Nullable<User>> => mockUser);
const updateUserById = jest.fn(async (..._args: unknown[]) => mockUser);
const findDefaultSignInExperience = jest.fn(async () => ({
...mockSignInExperience,
@ -34,7 +36,7 @@ jest.mock('@/lib/user', () => ({
jest.mock('@/queries/user', () => ({
findUserById: async () => findUserById(),
findUserByPhone: async () => mockUser,
findUserByPhone: async () => findUserByPhone(),
findUserByEmail: async () => findUserByEmail(),
updateUserById: async (...args: unknown[]) => updateUserById(...args),
hasUser: async (username: string) => username === 'username1',
@ -260,6 +262,7 @@ describe('session -> passwordlessRoutes', () => {
});
it('throw 404 (with flow `forgot-password`)', async () => {
findUserByPhone.mockResolvedValueOnce(null);
const response = await sessionRequest
.post('/session/passwordless/sms/verify')
.send({ phone: '13000000001', code: '1234', flow: PasscodeType.ForgotPassword });
@ -358,6 +361,7 @@ describe('session -> passwordlessRoutes', () => {
it('throw 404 (with flow `forgot-password`)', async () => {
const fakeTime = new Date();
jest.useFakeTimers().setSystemTime(fakeTime);
findUserByEmail.mockResolvedValueOnce(null);
const response = await sessionRequest
.post('/session/passwordless/email/verify')
.send({ email: 'b@a.com', code: '1234', flow: PasscodeType.ForgotPassword });
@ -501,6 +505,7 @@ describe('session -> passwordlessRoutes', () => {
},
},
});
findUserByPhone.mockResolvedValueOnce(null);
const response = await sessionRequest.post(`${signInRoute}/sms`);
expect(response.statusCode).toEqual(404);
});
@ -639,6 +644,7 @@ describe('session -> passwordlessRoutes', () => {
},
},
});
findUserByEmail.mockResolvedValueOnce(null);
const response = await sessionRequest.post(`${signInRoute}/email`);
expect(response.statusCode).toEqual(404);
});

View file

@ -6,12 +6,7 @@ import { object, string } from 'zod';
import RequestError from '@/errors/RequestError';
import { createPasscode, sendPasscode, verifyPasscode } from '@/lib/passcode';
import koaGuard from '@/middleware/koa-guard';
import {
findUserByEmail,
findUserByPhone,
hasUserWithEmail,
hasUserWithPhone,
} from '@/queries/user';
import { findUserByEmail, findUserByPhone } from '@/queries/user';
import { passcodeTypeGuard } from '@/routes/session/types';
import assertThat from '@/utils/assert-that';
@ -105,13 +100,10 @@ export default function passwordlessRoutes<T extends AnonymousRouter>(
await verifyPasscode(jti, flow, code, { phone });
if (flow === PasscodeType.ForgotPassword) {
assertThat(
await hasUserWithPhone(phone),
new RequestError({ code: 'user.phone_not_exists', status: 404 })
);
const user = await findUserByPhone(phone);
assertThat(user, new RequestError({ code: 'user.phone_not_exists', status: 404 }));
const { id } = await findUserByPhone(phone);
await assignVerificationResult(ctx, provider, { flow, userId: id });
await assignVerificationResult(ctx, provider, { flow, userId: user.id });
ctx.status = 204;
return next();
@ -156,14 +148,11 @@ export default function passwordlessRoutes<T extends AnonymousRouter>(
await verifyPasscode(jti, flow, code, { email });
if (flow === PasscodeType.ForgotPassword) {
assertThat(
await hasUserWithEmail(email),
new RequestError({ code: 'user.email_not_exists', status: 404 })
);
const user = await findUserByEmail(email);
const { id } = await findUserByEmail(email);
assertThat(user, new RequestError({ code: 'user.email_not_exists', status: 404 }));
await assignVerificationResult(ctx, provider, { flow, userId: id });
await assignVerificationResult(ctx, provider, { flow, userId: user.id });
ctx.status = 204;
return next();