From fb4c8cb1ed18278cb53e2d752460336603883dc7 Mon Sep 17 00:00:00 2001 From: Hongbo Wu Date: Thu, 27 Apr 2023 11:33:03 +0800 Subject: [PATCH 1/4] trim user email in mobile sign-up router --- .../api/src/routers/auth/mobile/sign_up.ts | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/packages/api/src/routers/auth/mobile/sign_up.ts b/packages/api/src/routers/auth/mobile/sign_up.ts index 55d9e6dea..350257d55 100644 --- a/packages/api/src/routers/auth/mobile/sign_up.ts +++ b/packages/api/src/routers/auth/mobile/sign_up.ts @@ -1,16 +1,16 @@ /* eslint-disable @typescript-eslint/restrict-template-expressions */ +import UserModel from '../../../datalayer/user' +import { createUser } from '../../../services/create_user' +import { hashPassword } from '../../../utils/auth' import { decodeAppleToken } from '../apple_auth' -import { decodeGoogleToken } from '../google_auth' import { + AuthProvider, DecodeTokenResult, JsonResponsePayload, - AuthProvider, PendingUserTokenPayload, } from '../auth_types' +import { decodeGoogleToken } from '../google_auth' import { createPendingUserToken, suggestedUsername } from '../jwt_helpers' -import UserModel from '../../../datalayer/user' -import { hashPassword } from '../../../utils/auth' -import { createUser } from '../../../services/create_user' export async function createMobileSignUpResponse( isAndroid: boolean, @@ -55,14 +55,16 @@ export async function createMobileEmailSignUpResponse( throw new Error('Missing username, password, name, or username') } + // trim whitespace in email address + const trimmedEmail = email.trim() const hashedPassword = await hashPassword(password) await createUser({ - email, + email: trimmedEmail, provider: 'EMAIL', - sourceUserId: email, - name, - username: username.toLowerCase(), + sourceUserId: trimmedEmail, + name: name.trim(), + username: username.trim().toLowerCase(), password: hashedPassword, pendingConfirmation: true, }) From 673c43aee0759bc98c0f50229bc1d7052e956714 Mon Sep 17 00:00:00 2001 From: Hongbo Wu Date: Thu, 27 Apr 2023 11:37:27 +0800 Subject: [PATCH 2/4] trim user email in mobile sign-in router --- .../api/src/routers/auth/mobile/sign_in.ts | 21 +++++++------------ .../api/src/routers/auth/mobile/sign_up.ts | 10 ++++----- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/packages/api/src/routers/auth/mobile/sign_in.ts b/packages/api/src/routers/auth/mobile/sign_in.ts index a19b505c3..323556569 100644 --- a/packages/api/src/routers/auth/mobile/sign_in.ts +++ b/packages/api/src/routers/auth/mobile/sign_in.ts @@ -1,18 +1,17 @@ /* eslint-disable @typescript-eslint/restrict-template-expressions */ +import UserModel from '../../../datalayer/user' +import { StatusType } from '../../../datalayer/user/model' +import { getUserByEmail } from '../../../services/create_user' +import { sendConfirmationEmail } from '../../../services/send_emails' +import { comparePassword } from '../../../utils/auth' import { decodeAppleToken } from '../apple_auth' -import { decodeGoogleToken } from '../google_auth' import { + AuthProvider, DecodeTokenResult, JsonResponsePayload, - AuthProvider, } from '../auth_types' +import { decodeGoogleToken } from '../google_auth' import { createMobileAuthPayload } from '../jwt_helpers' -import UserModel from '../../../datalayer/user' -import { initModels } from '../../../server' -import { sendConfirmationEmail } from '../../../services/send_emails' -import { kx } from '../../../datalayer/knex_config' -import { StatusType } from '../../../datalayer/user/model' -import { comparePassword } from '../../../utils/auth' export async function createMobileSignInResponse( isAndroid: boolean, @@ -46,11 +45,7 @@ export async function createMobileEmailSignInResponse( throw new Error('Missing username or password') } - const models = initModels(kx, false) - const user = await models.user.getWhere({ - email, - }) - + const user = await getUserByEmail(email.trim()) if (!user?.id || !user?.password) { throw new Error('user not found') } diff --git a/packages/api/src/routers/auth/mobile/sign_up.ts b/packages/api/src/routers/auth/mobile/sign_up.ts index 350257d55..c251558bd 100644 --- a/packages/api/src/routers/auth/mobile/sign_up.ts +++ b/packages/api/src/routers/auth/mobile/sign_up.ts @@ -1,16 +1,16 @@ /* eslint-disable @typescript-eslint/restrict-template-expressions */ -import UserModel from '../../../datalayer/user' -import { createUser } from '../../../services/create_user' -import { hashPassword } from '../../../utils/auth' import { decodeAppleToken } from '../apple_auth' +import { decodeGoogleToken } from '../google_auth' import { - AuthProvider, DecodeTokenResult, JsonResponsePayload, + AuthProvider, PendingUserTokenPayload, } from '../auth_types' -import { decodeGoogleToken } from '../google_auth' import { createPendingUserToken, suggestedUsername } from '../jwt_helpers' +import UserModel from '../../../datalayer/user' +import { hashPassword } from '../../../utils/auth' +import { createUser } from '../../../services/create_user' export async function createMobileSignUpResponse( isAndroid: boolean, From 0e8887a1d9d4453897224e74a97d94120490e7e2 Mon Sep 17 00:00:00 2001 From: Hongbo Wu Date: Thu, 27 Apr 2023 11:39:34 +0800 Subject: [PATCH 3/4] trim user email before saving in db --- packages/api/src/services/create_user.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/api/src/services/create_user.ts b/packages/api/src/services/create_user.ts index f8e2fbea2..ec180f5be 100644 --- a/packages/api/src/services/create_user.ts +++ b/packages/api/src/services/create_user.ts @@ -24,7 +24,8 @@ export const createUser = async (input: { password?: string pendingConfirmation?: boolean }): Promise<[User, Profile]> => { - const existingUser = await getUserByEmail(input.email) + const trimmedEmail = input.email.trim() + const existingUser = await getUserByEmail(trimmedEmail) if (existingUser) { if (existingUser.profile) { return Promise.reject({ errorCode: SignupErrorCode.UserExists }) @@ -63,7 +64,7 @@ export const createUser = async (input: { const user = await t.getRepository(User).save({ source: input.provider, name: input.name, - email: input.email, + email: trimmedEmail, sourceUserId: input.sourceUserId, password: input.password, status: input.pendingConfirmation From 31cbcc7c7f98552ec00aa527aae8079fee7f3b94 Mon Sep 17 00:00:00 2001 From: Hongbo Wu Date: Thu, 27 Apr 2023 12:18:32 +0800 Subject: [PATCH 4/4] validate sign up request body in mobile sign-up router --- packages/api/src/routers/auth/auth_router.ts | 42 ++++++++++--------- .../routers/auth/mobile/mobile_auth_router.ts | 9 +--- .../api/src/routers/auth/mobile/sign_up.ts | 9 ++-- 3 files changed, 28 insertions(+), 32 deletions(-) diff --git a/packages/api/src/routers/auth/auth_router.ts b/packages/api/src/routers/auth/auth_router.ts index 8721fb9ca..0b4b5266c 100644 --- a/packages/api/src/routers/auth/auth_router.ts +++ b/packages/api/src/routers/auth/auth_router.ts @@ -54,6 +54,15 @@ import { import { createWebAuthToken } from './jwt_helpers' import { createSsoToken, ssoRedirectURL } from '../../utils/sso' +export interface SignupRequest { + email: string + password: string + name: string + username: string + bio?: string + pictureUrl?: string +} + const logger = buildLogger('app.dispatch') const signToken = promisify(jwt.sign) @@ -62,6 +71,19 @@ const cookieParams = { maxAge: 365 * 24 * 60 * 60 * 1000, } +export const isValidSignupRequest = (obj: any): obj is SignupRequest => { + return ( + 'email' in obj && + obj.email.trim().length > 0 && // email must not be empty + 'password' in obj && + obj.password.length >= 8 && // password must be at least 8 characters + 'name' in obj && + obj.name.trim().length > 0 && // name must not be empty + 'username' in obj && + obj.username.trim().length > 0 // username must not be empty + ) +} + export function authRouter() { const router = express.Router() @@ -443,26 +465,6 @@ export function authRouter() { '/email-signup', cors(corsConfig), async (req: express.Request, res: express.Response) => { - interface SignupRequest { - email: string - password: string - name: string - username: string - bio?: string - pictureUrl?: string - } - function isValidSignupRequest(obj: any): obj is SignupRequest { - return ( - 'email' in obj && - obj.email.trim().length > 0 && // email must not be empty - 'password' in obj && - obj.password.length >= 8 && // password must be at least 8 characters - 'name' in obj && - obj.name.trim().length > 0 && // name must not be empty - 'username' in obj && - obj.username.trim().length > 0 // username must not be empty - ) - } if (!isValidSignupRequest(req.body)) { return res.redirect( `${env.client.url}/auth/email-signup?errorCodes=INVALID_CREDENTIALS` diff --git a/packages/api/src/routers/auth/mobile/mobile_auth_router.ts b/packages/api/src/routers/auth/mobile/mobile_auth_router.ts index c43df9bc8..f27a032fa 100644 --- a/packages/api/src/routers/auth/mobile/mobile_auth_router.ts +++ b/packages/api/src/routers/auth/mobile/mobile_auth_router.ts @@ -32,13 +32,8 @@ export function mobileAuthRouter() { }) router.post('/email-sign-up', async (req, res) => { - const { email, password, username, name } = req.body - const payload = await createMobileEmailSignUpResponse( - email, - password, - username, - name - ) + const payload = await createMobileEmailSignUpResponse(req.body) + res.status(payload.statusCode).json(payload.json) }) diff --git a/packages/api/src/routers/auth/mobile/sign_up.ts b/packages/api/src/routers/auth/mobile/sign_up.ts index c251558bd..f21e64d5d 100644 --- a/packages/api/src/routers/auth/mobile/sign_up.ts +++ b/packages/api/src/routers/auth/mobile/sign_up.ts @@ -11,6 +11,7 @@ import { createPendingUserToken, suggestedUsername } from '../jwt_helpers' import UserModel from '../../../datalayer/user' import { hashPassword } from '../../../utils/auth' import { createUser } from '../../../services/create_user' +import { isValidSignupRequest } from '../auth_router' export async function createMobileSignUpResponse( isAndroid: boolean, @@ -45,15 +46,13 @@ export async function createMobileSignUpResponse( } export async function createMobileEmailSignUpResponse( - email?: string, - password?: string, - username?: string, - name?: string + requestBody: any ): Promise { try { - if (!email || !password || !username || !name) { + if (!isValidSignupRequest(requestBody)) { throw new Error('Missing username, password, name, or username') } + const { email, password, name, username } = requestBody // trim whitespace in email address const trimmedEmail = email.trim()