0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-16 20:26:19 -05:00

refactor(core): fix

This commit is contained in:
Darcy Ye 2022-09-30 00:33:03 +08:00
parent b35fe0811d
commit 07485a6e6c
No known key found for this signature in database
GPG key ID: B46F4C07EDEFC610
4 changed files with 49 additions and 55 deletions

View file

@ -436,8 +436,8 @@ describe('session -> passwordlessRoutes', () => {
}); });
describe('POST /session/register/passwordless/sms', () => { describe('POST /session/register/passwordless/sms', () => {
afterEach(() => { beforeEach(() => {
jest.clearAllMocks(); jest.resetAllMocks();
}); });
it('should call interactionResult', async () => { it('should call interactionResult', async () => {
interactionDetails.mockResolvedValueOnce({ interactionDetails.mockResolvedValueOnce({
@ -513,8 +513,8 @@ describe('session -> passwordlessRoutes', () => {
}); });
describe('POST /session/register/passwordless/email', () => { describe('POST /session/register/passwordless/email', () => {
afterEach(() => { beforeEach(() => {
jest.clearAllMocks(); jest.resetAllMocks();
}); });
it('should call interactionResult', async () => { it('should call interactionResult', async () => {
interactionDetails.mockResolvedValueOnce({ interactionDetails.mockResolvedValueOnce({

View file

@ -15,16 +15,16 @@ import {
hasUserWithEmail, hasUserWithEmail,
hasUserWithPhone, hasUserWithPhone,
} from '@/queries/user'; } from '@/queries/user';
import { verificationStorageParser, flowTypeGuard } from '@/routes/session/types'; import { flowTypeGuard } from '@/routes/session/types';
import assertThat from '@/utils/assert-that'; import assertThat from '@/utils/assert-that';
import { AnonymousRouter } from '../types'; import { AnonymousRouter } from '../types';
import { verificationTimeout } from './consts'; import { verificationTimeout } from './consts';
import { import {
getAndCheckVerificationStorage,
getRoutePrefix, getRoutePrefix,
getPasscodeType, getPasscodeType,
getPasswordlessRelatedLogType, getPasswordlessRelatedLogType,
verificationSessionCheckByFlow,
} from './utils'; } from './utils';
export const registerRoute = getRoutePrefix('register', 'passwordless'); export const registerRoute = getRoutePrefix('register', 'passwordless');
@ -157,14 +157,8 @@ export default function passwordlessRoutes<T extends AnonymousRouter>(
); );
router.post(`${signInRoute}/sms`, async (ctx, next) => { 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'); const type = getPasswordlessRelatedLogType('sign-in', 'sms');
ctx.log(type, { phone, flow, expiresAt }); const { phone } = await getAndCheckVerificationStorage(ctx, provider, type, 'sign-in');
verificationSessionCheckByFlow('sign-in', { flow, expiresAt });
assertThat( assertThat(
phone && (await hasUserWithPhone(phone)), phone && (await hasUserWithPhone(phone)),
@ -181,14 +175,8 @@ export default function passwordlessRoutes<T extends AnonymousRouter>(
}); });
router.post(`${signInRoute}/email`, async (ctx, next) => { 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'); const type = getPasswordlessRelatedLogType('sign-in', 'email');
ctx.log(type, { email, flow, expiresAt }); const { email } = await getAndCheckVerificationStorage(ctx, provider, type, 'sign-in');
verificationSessionCheckByFlow('sign-in', { flow, expiresAt });
assertThat( assertThat(
email && (await hasUserWithEmail(email)), email && (await hasUserWithEmail(email)),
@ -205,14 +193,8 @@ export default function passwordlessRoutes<T extends AnonymousRouter>(
}); });
router.post(`${registerRoute}/sms`, async (ctx, next) => { 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'); const type = getPasswordlessRelatedLogType('register', 'sms');
ctx.log(type, { phone, flow, expiresAt }); const { phone } = await getAndCheckVerificationStorage(ctx, provider, type, 'register');
verificationSessionCheckByFlow('register', { flow, expiresAt });
assertThat( assertThat(
phone && !(await hasUserWithPhone(phone)), phone && !(await hasUserWithPhone(phone)),
@ -229,14 +211,8 @@ export default function passwordlessRoutes<T extends AnonymousRouter>(
}); });
router.post(`${registerRoute}/email`, async (ctx, next) => { 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'); const type = getPasswordlessRelatedLogType('register', 'email');
ctx.log(type, { email, flow, expiresAt }); const { email } = await getAndCheckVerificationStorage(ctx, provider, type, 'register');
verificationSessionCheckByFlow('register', { flow, expiresAt });
assertThat( assertThat(
email && !(await hasUserWithEmail(email)), email && !(await hasUserWithEmail(email)),

View file

@ -1,8 +1,5 @@
import { z } from 'zod'; 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 const flowTypeGuard = z.enum(['sign-in', 'register', 'forgot-password']);
export type FlowType = z.infer<typeof flowTypeGuard>; export type FlowType = z.infer<typeof flowTypeGuard>;
@ -25,20 +22,3 @@ export const verificationStorageGuard = z.object({
}); });
export type VerificationStorage = z.infer<typeof verificationStorageGuard>; export type VerificationStorage = z.infer<typeof verificationStorageGuard>;
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;
};

View file

@ -2,11 +2,15 @@ import { logTypeGuard, LogType, PasscodeType } from '@logto/schemas';
import { Truthy } from '@silverhand/essentials'; import { Truthy } from '@silverhand/essentials';
import camelcase from 'camelcase'; import camelcase from 'camelcase';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { Context } from 'koa';
import { Provider } from 'oidc-provider';
import { z } from 'zod';
import RequestError from '@/errors/RequestError'; import RequestError from '@/errors/RequestError';
import { LogContext } from '@/middleware/koa-log';
import assertThat from '@/utils/assert-that'; import assertThat from '@/utils/assert-that';
import { FlowType, Operation, VerificationStorage, Via } from './types'; import { verificationStorageGuard, FlowType, Operation, VerificationStorage, Via } from './types';
export const getRoutePrefix = ( export const getRoutePrefix = (
type: FlowType, type: FlowType,
@ -46,6 +50,24 @@ export const getPasswordlessRelatedLogType = (
return result.data; 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 = ( export const verificationSessionCheckByFlow = (
currentFlow: FlowType, currentFlow: FlowType,
payload: Pick<VerificationStorage, 'flow' | 'expiresAt'> payload: Pick<VerificationStorage, 'flow' | 'expiresAt'>
@ -62,3 +84,19 @@ export const verificationSessionCheckByFlow = (
new RequestError({ code: 'session.verification_expired', status: 401 }) new RequestError({ code: 'session.verification_expired', status: 401 })
); );
}; };
export const getAndCheckVerificationStorage = async (
ctx: Context & LogContext,
provider: Provider,
logType: LogType,
flowType: FlowType
): Promise<Pick<VerificationStorage, 'email' | 'phone'>> => {
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 };
};