From 07485a6e6cc53a879229139e7bbca1511a2a510e Mon Sep 17 00:00:00 2001 From: Darcy Ye Date: Fri, 30 Sep 2022 00:33:03 +0800 Subject: [PATCH] refactor(core): fix --- .../src/routes/session/passwordless.test.ts | 8 ++-- .../core/src/routes/session/passwordless.ts | 36 +++-------------- packages/core/src/routes/session/types.ts | 20 ---------- packages/core/src/routes/session/utils.ts | 40 ++++++++++++++++++- 4 files changed, 49 insertions(+), 55 deletions(-) diff --git a/packages/core/src/routes/session/passwordless.test.ts b/packages/core/src/routes/session/passwordless.test.ts index 7fe22ea5d..9001976bb 100644 --- a/packages/core/src/routes/session/passwordless.test.ts +++ b/packages/core/src/routes/session/passwordless.test.ts @@ -436,8 +436,8 @@ describe('session -> passwordlessRoutes', () => { }); describe('POST /session/register/passwordless/sms', () => { - afterEach(() => { - jest.clearAllMocks(); + beforeEach(() => { + jest.resetAllMocks(); }); it('should call interactionResult', async () => { interactionDetails.mockResolvedValueOnce({ @@ -513,8 +513,8 @@ describe('session -> passwordlessRoutes', () => { }); describe('POST /session/register/passwordless/email', () => { - afterEach(() => { - jest.clearAllMocks(); + beforeEach(() => { + jest.resetAllMocks(); }); it('should call interactionResult', async () => { interactionDetails.mockResolvedValueOnce({ diff --git a/packages/core/src/routes/session/passwordless.ts b/packages/core/src/routes/session/passwordless.ts index 663e4f5ed..f9a4f91cc 100644 --- a/packages/core/src/routes/session/passwordless.ts +++ b/packages/core/src/routes/session/passwordless.ts @@ -15,16 +15,16 @@ import { hasUserWithEmail, hasUserWithPhone, } from '@/queries/user'; -import { verificationStorageParser, flowTypeGuard } from '@/routes/session/types'; +import { flowTypeGuard } from '@/routes/session/types'; import assertThat from '@/utils/assert-that'; import { AnonymousRouter } from '../types'; import { verificationTimeout } from './consts'; import { + getAndCheckVerificationStorage, getRoutePrefix, getPasscodeType, getPasswordlessRelatedLogType, - verificationSessionCheckByFlow, } from './utils'; export const registerRoute = getRoutePrefix('register', 'passwordless'); @@ -157,14 +157,8 @@ export default function passwordlessRoutes( ); router.post(`${signInRoute}/sms`, async (ctx, next) => { - const { result } = await provider.interactionDetails(ctx.req, ctx.res); - - const { phone, flow, expiresAt } = verificationStorageParser(result); - const type = getPasswordlessRelatedLogType('sign-in', 'sms'); - ctx.log(type, { phone, flow, expiresAt }); - - verificationSessionCheckByFlow('sign-in', { flow, expiresAt }); + const { phone } = await getAndCheckVerificationStorage(ctx, provider, type, 'sign-in'); assertThat( phone && (await hasUserWithPhone(phone)), @@ -181,14 +175,8 @@ export default function passwordlessRoutes( }); router.post(`${signInRoute}/email`, async (ctx, next) => { - const { result } = await provider.interactionDetails(ctx.req, ctx.res); - - const { email, flow, expiresAt } = verificationStorageParser(result); - const type = getPasswordlessRelatedLogType('sign-in', 'email'); - ctx.log(type, { email, flow, expiresAt }); - - verificationSessionCheckByFlow('sign-in', { flow, expiresAt }); + const { email } = await getAndCheckVerificationStorage(ctx, provider, type, 'sign-in'); assertThat( email && (await hasUserWithEmail(email)), @@ -205,14 +193,8 @@ export default function passwordlessRoutes( }); router.post(`${registerRoute}/sms`, async (ctx, next) => { - const { result } = await provider.interactionDetails(ctx.req, ctx.res); - - const { phone, flow, expiresAt } = verificationStorageParser(result); - const type = getPasswordlessRelatedLogType('register', 'sms'); - ctx.log(type, { phone, flow, expiresAt }); - - verificationSessionCheckByFlow('register', { flow, expiresAt }); + const { phone } = await getAndCheckVerificationStorage(ctx, provider, type, 'register'); assertThat( phone && !(await hasUserWithPhone(phone)), @@ -229,14 +211,8 @@ export default function passwordlessRoutes( }); router.post(`${registerRoute}/email`, async (ctx, next) => { - const { result } = await provider.interactionDetails(ctx.req, ctx.res); - - const { email, flow, expiresAt } = verificationStorageParser(result); - const type = getPasswordlessRelatedLogType('register', 'email'); - ctx.log(type, { email, flow, expiresAt }); - - verificationSessionCheckByFlow('register', { flow, expiresAt }); + const { email } = await getAndCheckVerificationStorage(ctx, provider, type, 'register'); assertThat( email && !(await hasUserWithEmail(email)), diff --git a/packages/core/src/routes/session/types.ts b/packages/core/src/routes/session/types.ts index 4cd46597e..d9d9c3a81 100644 --- a/packages/core/src/routes/session/types.ts +++ b/packages/core/src/routes/session/types.ts @@ -1,8 +1,5 @@ import { z } from 'zod'; -import RequestError from '@/errors/RequestError'; -import assertThat from '@/utils/assert-that'; - export const flowTypeGuard = z.enum(['sign-in', 'register', 'forgot-password']); export type FlowType = z.infer; @@ -25,20 +22,3 @@ export const verificationStorageGuard = z.object({ }); export type VerificationStorage = z.infer; - -export const verificationStorageParser = (data: unknown): VerificationStorage => { - const verificationResult = z - .object({ - verification: verificationStorageGuard, - }) - .safeParse(data); - assertThat( - verificationResult.success, - new RequestError({ - code: 'session.verification_session_not_found', - status: 404, - }) - ); - - return verificationResult.data.verification; -}; diff --git a/packages/core/src/routes/session/utils.ts b/packages/core/src/routes/session/utils.ts index a6b3a564b..92656b2d1 100644 --- a/packages/core/src/routes/session/utils.ts +++ b/packages/core/src/routes/session/utils.ts @@ -2,11 +2,15 @@ import { logTypeGuard, LogType, PasscodeType } from '@logto/schemas'; import { Truthy } from '@silverhand/essentials'; import camelcase from 'camelcase'; import dayjs from 'dayjs'; +import { Context } from 'koa'; +import { Provider } from 'oidc-provider'; +import { z } from 'zod'; import RequestError from '@/errors/RequestError'; +import { LogContext } from '@/middleware/koa-log'; import assertThat from '@/utils/assert-that'; -import { FlowType, Operation, VerificationStorage, Via } from './types'; +import { verificationStorageGuard, FlowType, Operation, VerificationStorage, Via } from './types'; export const getRoutePrefix = ( type: FlowType, @@ -46,6 +50,24 @@ export const getPasswordlessRelatedLogType = ( return result.data; }; +export const verificationStorageParser = (data: unknown): VerificationStorage => { + const verificationResult = z + .object({ + verification: verificationStorageGuard, + }) + .safeParse(data); + + assertThat( + verificationResult.success, + new RequestError({ + code: 'session.verification_session_not_found', + status: 404, + }) + ); + + return verificationResult.data.verification; +}; + export const verificationSessionCheckByFlow = ( currentFlow: FlowType, payload: Pick @@ -62,3 +84,19 @@ export const verificationSessionCheckByFlow = ( new RequestError({ code: 'session.verification_expired', status: 401 }) ); }; + +export const getAndCheckVerificationStorage = async ( + ctx: Context & LogContext, + provider: Provider, + logType: LogType, + flowType: FlowType +): Promise> => { + const { result } = await provider.interactionDetails(ctx.req, ctx.res); + const { email, phone, flow, expiresAt } = verificationStorageParser(result); + + ctx.log(logType, { email, phone, flow, expiresAt }); + + verificationSessionCheckByFlow(flowType, { flow, expiresAt }); + + return { email, phone }; +};