From 9e677ca97ad97699d0dad527133e4dac9ee493e6 Mon Sep 17 00:00:00 2001 From: Darcy Ye Date: Tue, 8 Mar 2022 16:09:06 +0800 Subject: [PATCH] feat(core): post /session/forgot-password/phone/verify-passcode-and-reset-password (#334) * feat(core): add post /session/forgot-password/phone/verify-passcode and UT * feat(core): reset password once passcode verification succeed --- packages/core/src/routes/session.test.ts | 40 ++++++++++++++++++++++++ packages/core/src/routes/session.ts | 37 ++++++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/packages/core/src/routes/session.test.ts b/packages/core/src/routes/session.test.ts index 3f479c46d..0c282e2de 100644 --- a/packages/core/src/routes/session.test.ts +++ b/packages/core/src/routes/session.test.ts @@ -780,6 +780,46 @@ describe('sessionRoutes', () => { }); }); + describe('POST /session/forgot-password/phone/verify-passcode-and-reset-password', () => { + beforeAll(() => { + interactionDetails.mockResolvedValueOnce({ + jti: 'jti', + }); + }); + + it('throw if no user can be found with phone', async () => { + const response = await sessionRequest + .post('/session/forgot-password/phone/verify-passcode-and-reset-password') + .send({ phone: '13000000001', code: '1234', password: '123456' }); + expect(response).toHaveProperty('statusCode', 422); + }); + + it('fail to verify passcode', async () => { + const response = await sessionRequest + .post('/session/forgot-password/phone/verify-passcode-and-reset-password') + .send({ phone: '13000000000', code: '1231', password: '123456' }); + expect(response).toHaveProperty('statusCode', 400); + }); + + it('verify passcode, reset password and assign result', async () => { + const response = await sessionRequest + .post('/session/forgot-password/phone/verify-passcode-and-reset-password') + .send({ phone: '13000000000', code: '1234', password: '123456' }); + expect(response).toHaveProperty('statusCode', 200); + expect(updateUserById).toHaveBeenCalledWith('id', { + passwordEncryptionSalt: 'user1', + passwordEncrypted: 'id_123456_user1', + passwordEncryptionMethod: 'SaltAndPepper', + }); + expect(interactionResult).toHaveBeenCalledWith( + expect.anything(), + expect.anything(), + expect.objectContaining({ login: { accountId: 'id' } }), + expect.anything() + ); + }); + }); + describe('POST /session/bind-social', () => { it('throw if session is not authorized', async () => { interactionDetails.mockResolvedValueOnce({}); diff --git a/packages/core/src/routes/session.ts b/packages/core/src/routes/session.ts index b4a1a858c..84e78633d 100644 --- a/packages/core/src/routes/session.ts +++ b/packages/core/src/routes/session.ts @@ -503,6 +503,43 @@ export default function sessionRoutes(router: T, prov } ); + router.post( + '/session/forgot-password/phone/verify-passcode-and-reset-password', + koaGuard({ + body: object({ + phone: string().regex(phoneRegEx), + code: string(), + password: string().regex(passwordRegEx), + }), + }), + async (ctx, next) => { + const { jti } = await provider.interactionDetails(ctx.req, ctx.res); + const { phone, code, password } = ctx.guard.body; + ctx.userLog.phone = phone; + ctx.userLog.type = UserLogType.ForgotPasswordPhone; + + assertThat( + await hasUserWithPhone(phone), + new RequestError({ code: 'user.phone_not_exists', status: 422 }) + ); + + await verifyPasscode(jti, PasscodeType.ForgotPassword, code, { phone }); + const { id } = await findUserByPhone(phone); + ctx.userLog.userId = id; + + const { passwordEncryptionSalt, passwordEncrypted, passwordEncryptionMethod } = + encryptUserPassword(id, password); + await updateUserById(id, { + passwordEncryptionSalt, + passwordEncrypted, + passwordEncryptionMethod, + }); + await assignInteractionResults(ctx, provider, { login: { accountId: id } }); + + return next(); + } + ); + router.post( '/session/bind-social', koaGuard({