0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-03-10 22:22:45 -05:00

chore(core): username/phone/email validity checks to koaGuard middleware (in /session routes) (#285)

* feat(core): all username/phone/email validity checks are put in koaGuard middleware

* feat(core): remove unnecessary comments

* feat(core): unwrap unnecessary methods
This commit is contained in:
Darcy Ye 2022-02-25 10:50:50 +08:00 committed by GitHub
parent bd6dc4261c
commit 5fa6e2b280
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 52 additions and 70 deletions

View file

@ -1,11 +1,6 @@
import { Context } from 'koa';
import { InteractionResults, Provider } from 'oidc-provider';
import RequestError from '@/errors/RequestError';
import { hasUserWithEmail, hasUserWithPhone } from '@/queries/user';
import assertThat from '@/utils/assert-that';
import { emailRegEx, phoneRegEx } from '@/utils/regex';
// TODO: change this after frontend is ready.
// Should combine baseUrl(domain) from database with a 'callback' endpoint.
export const connectorRedirectUrl = 'https://logto.dev/callback';
@ -21,35 +16,3 @@ export const assignInteractionResults = async (
});
ctx.body = { redirectTo };
};
export const checkEmailValidityAndAvailability = async (email: string) => {
assertThat(emailRegEx.test(email), new RequestError('user.invalid_email'));
assertThat(
!(await hasUserWithEmail(email)),
new RequestError({ code: 'user.email_exists_register', status: 422 })
);
};
export const checkEmailValidityAndExistence = async (email: string) => {
assertThat(emailRegEx.test(email), new RequestError('user.invalid_email'));
assertThat(
await hasUserWithEmail(email),
new RequestError({ code: 'user.email_not_exists', status: 422 })
);
};
export const checkPhoneNumberValidityAndAvailability = async (phone: string) => {
assertThat(phoneRegEx.test(phone), new RequestError('user.invalid_phone'));
assertThat(
!(await hasUserWithPhone(phone)),
new RequestError({ code: 'user.phone_exists_register', status: 422 })
);
};
export const checkPhoneNumberValidityAndExistence = async (phone: string) => {
assertThat(phoneRegEx.test(phone), new RequestError('user.invalid_phone'));
assertThat(
await hasUserWithPhone(phone),
new RequestError({ code: 'user.phone_not_exists', status: 422 })
);
};

View file

@ -10,14 +10,7 @@ import { object, string } from 'zod';
import { getSocialConnectorInstanceById } from '@/connectors';
import RequestError from '@/errors/RequestError';
import { createPasscode, sendPasscode, verifyPasscode } from '@/lib/passcode';
import {
assignInteractionResults,
connectorRedirectUrl,
checkEmailValidityAndAvailability,
checkEmailValidityAndExistence,
checkPhoneNumberValidityAndAvailability,
checkPhoneNumberValidityAndExistence,
} from '@/lib/session';
import { assignInteractionResults, connectorRedirectUrl } from '@/lib/session';
import {
findSocialRelatedUser,
getUserInfoByAuthCode,
@ -26,6 +19,8 @@ import {
import { encryptUserPassword, generateUserId, findUserByUsernameAndPassword } from '@/lib/user';
import koaGuard from '@/middleware/koa-guard';
import {
hasUserWithEmail,
hasUserWithPhone,
hasUser,
hasUserWithIdentity,
insertUser,
@ -36,7 +31,7 @@ import {
findUserByIdentity,
} from '@/queries/user';
import assertThat from '@/utils/assert-that';
import { usernameRegEx } from '@/utils/regex';
import { emailRegEx, phoneRegEx, usernameRegEx } from '@/utils/regex';
import { AnonymousRouter } from './types';
@ -57,13 +52,13 @@ export default function sessionRoutes<T extends AnonymousRouter>(router: T, prov
router.post(
'/session/sign-in/username-password',
koaGuard({ body: object({ username: string(), password: string() }) }),
koaGuard({ body: object({ username: string().regex(usernameRegEx), password: string() }) }),
async (ctx, next) => {
ctx.userLog.type = UserLogType.SignInUsernameAndPassword;
const { username, password } = ctx.guard.body;
assertThat(username && password, 'session.insufficient_info');
ctx.userLog.username = username;
ctx.userLog.type = UserLogType.SignInUsernameAndPassword;
assertThat(password, 'session.insufficient_info');
const { id } = await findUserByUsernameAndPassword(username, password);
ctx.userLog.userId = id;
@ -76,14 +71,17 @@ export default function sessionRoutes<T extends AnonymousRouter>(router: T, prov
router.post(
'/session/sign-in/passwordless/phone/send-passcode',
koaGuard({ body: object({ phone: string() }) }),
koaGuard({ body: object({ phone: string().regex(phoneRegEx) }) }),
async (ctx, next) => {
const { jti } = await provider.interactionDetails(ctx.req, ctx.res);
const { phone } = ctx.guard.body;
ctx.userLog.phone = phone;
ctx.userLog.type = UserLogType.SignInPhone;
await checkPhoneNumberValidityAndExistence(phone);
assertThat(
await hasUserWithPhone(phone),
new RequestError({ code: 'user.phone_not_exists', status: 422 })
);
const passcode = await createPasscode(jti, PasscodeType.SignIn, { phone });
await sendPasscode(passcode);
@ -95,14 +93,17 @@ export default function sessionRoutes<T extends AnonymousRouter>(router: T, prov
router.post(
'/session/sign-in/passwordless/phone/verify-passcode',
koaGuard({ body: object({ phone: string(), code: string() }) }),
koaGuard({ body: object({ phone: string().regex(phoneRegEx), code: string() }) }),
async (ctx, next) => {
const { jti } = await provider.interactionDetails(ctx.req, ctx.res);
const { phone, code } = ctx.guard.body;
ctx.userLog.phone = phone;
ctx.userLog.type = UserLogType.SignInPhone;
await checkPhoneNumberValidityAndExistence(phone);
assertThat(
await hasUserWithPhone(phone),
new RequestError({ code: 'user.phone_not_exists', status: 422 })
);
await verifyPasscode(jti, PasscodeType.SignIn, code, { phone });
const { id } = await findUserByPhone(phone);
@ -116,14 +117,17 @@ export default function sessionRoutes<T extends AnonymousRouter>(router: T, prov
router.post(
'/session/sign-in/passwordless/email/send-passcode',
koaGuard({ body: object({ email: string() }) }),
koaGuard({ body: object({ email: string().regex(emailRegEx) }) }),
async (ctx, next) => {
const { jti } = await provider.interactionDetails(ctx.req, ctx.res);
const { email } = ctx.guard.body;
ctx.userLog.email = email;
ctx.userLog.type = UserLogType.SignInEmail;
await checkEmailValidityAndExistence(email);
assertThat(
await hasUserWithEmail(email),
new RequestError({ code: 'user.email_not_exists', status: 422 })
);
const passcode = await createPasscode(jti, PasscodeType.SignIn, { email });
await sendPasscode(passcode);
@ -135,14 +139,17 @@ export default function sessionRoutes<T extends AnonymousRouter>(router: T, prov
router.post(
'/session/sign-in/passwordless/email/verify-passcode',
koaGuard({ body: object({ email: string(), code: string() }) }),
koaGuard({ body: object({ email: string().regex(emailRegEx), code: string() }) }),
async (ctx, next) => {
const { jti } = await provider.interactionDetails(ctx.req, ctx.res);
const { email, code } = ctx.guard.body;
ctx.userLog.email = email;
ctx.userLog.type = UserLogType.SignInEmail;
await checkEmailValidityAndExistence(email);
assertThat(
await hasUserWithEmail(email),
new RequestError({ code: 'user.email_not_exists', status: 422 })
);
await verifyPasscode(jti, PasscodeType.SignIn, code, { email });
const { id } = await findUserByEmail(email);
@ -269,13 +276,14 @@ export default function sessionRoutes<T extends AnonymousRouter>(router: T, prov
router.post(
'/session/register/username-password',
koaGuard({ body: object({ username: string(), password: string() }) }),
koaGuard({ body: object({ username: string().regex(usernameRegEx), password: string() }) }),
async (ctx, next) => {
ctx.userLog.type = UserLogType.RegisterUsernameAndPassword;
const { username, password } = ctx.guard.body;
ctx.userLog.username = username;
ctx.userLog.type = UserLogType.RegisterUsernameAndPassword;
assertThat(
username && password,
password,
new RequestError({
code: 'session.insufficient_info',
status: 400,
@ -288,7 +296,6 @@ export default function sessionRoutes<T extends AnonymousRouter>(router: T, prov
status: 422,
})
);
ctx.userLog.username = username;
const id = await generateUserId();
ctx.userLog.userId = id;
@ -323,14 +330,17 @@ export default function sessionRoutes<T extends AnonymousRouter>(router: T, prov
router.post(
'/session/register/passwordless/phone/send-passcode',
koaGuard({ body: object({ phone: string() }) }),
koaGuard({ body: object({ phone: string().regex(phoneRegEx) }) }),
async (ctx, next) => {
ctx.userLog.type = UserLogType.RegisterPhone;
const { jti } = await provider.interactionDetails(ctx.req, ctx.res);
const { phone } = ctx.guard.body;
ctx.userLog.phone = phone;
await checkPhoneNumberValidityAndAvailability(phone);
assertThat(
!(await hasUserWithPhone(phone)),
new RequestError({ code: 'user.phone_exists_register', status: 422 })
);
const passcode = await createPasscode(jti, PasscodeType.Register, { phone });
await sendPasscode(passcode);
@ -342,14 +352,17 @@ export default function sessionRoutes<T extends AnonymousRouter>(router: T, prov
router.post(
'/session/register/passwordless/phone/verify-passcode',
koaGuard({ body: object({ phone: string(), code: string() }) }),
koaGuard({ body: object({ phone: string().regex(phoneRegEx), code: string() }) }),
async (ctx, next) => {
const { jti } = await provider.interactionDetails(ctx.req, ctx.res);
const { phone, code } = ctx.guard.body;
ctx.userLog.phone = phone;
ctx.userLog.type = UserLogType.RegisterPhone;
await checkPhoneNumberValidityAndAvailability(phone);
assertThat(
!(await hasUserWithPhone(phone)),
new RequestError({ code: 'user.phone_exists_register', status: 422 })
);
await verifyPasscode(jti, PasscodeType.Register, code, { phone });
const id = await generateUserId();
@ -364,14 +377,17 @@ export default function sessionRoutes<T extends AnonymousRouter>(router: T, prov
router.post(
'/session/register/passwordless/email/send-passcode',
koaGuard({ body: object({ email: string() }) }),
koaGuard({ body: object({ email: string().regex(emailRegEx) }) }),
async (ctx, next) => {
const { jti } = await provider.interactionDetails(ctx.req, ctx.res);
const { email } = ctx.guard.body;
ctx.userLog.email = email;
ctx.userLog.type = UserLogType.RegisterEmail;
await checkEmailValidityAndAvailability(email);
assertThat(
!(await hasUserWithEmail(email)),
new RequestError({ code: 'user.email_exists_register', status: 422 })
);
const passcode = await createPasscode(jti, PasscodeType.Register, { email });
await sendPasscode(passcode);
@ -383,14 +399,17 @@ export default function sessionRoutes<T extends AnonymousRouter>(router: T, prov
router.post(
'/session/register/passwordless/email/verify-passcode',
koaGuard({ body: object({ email: string(), code: string() }) }),
koaGuard({ body: object({ email: string().regex(emailRegEx), code: string() }) }),
async (ctx, next) => {
const { jti } = await provider.interactionDetails(ctx.req, ctx.res);
const { email, code } = ctx.guard.body;
ctx.userLog.email = email;
ctx.userLog.type = UserLogType.RegisterEmail;
await checkEmailValidityAndAvailability(email);
assertThat(
!(await hasUserWithEmail(email)),
new RequestError({ code: 'user.email_exists_register', status: 422 })
);
await verifyPasscode(jti, PasscodeType.Register, code, { email });
const id = await generateUserId();