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:
parent
b35fe0811d
commit
07485a6e6c
4 changed files with 49 additions and 55 deletions
|
@ -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({
|
||||
|
|
|
@ -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<T extends AnonymousRouter>(
|
|||
);
|
||||
|
||||
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<T extends AnonymousRouter>(
|
|||
});
|
||||
|
||||
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<T extends AnonymousRouter>(
|
|||
});
|
||||
|
||||
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<T extends AnonymousRouter>(
|
|||
});
|
||||
|
||||
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)),
|
||||
|
|
|
@ -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<typeof flowTypeGuard>;
|
||||
|
@ -25,20 +22,3 @@ export const verificationStorageGuard = z.object({
|
|||
});
|
||||
|
||||
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;
|
||||
};
|
||||
|
|
|
@ -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<VerificationStorage, 'flow' | 'expiresAt'>
|
||||
|
@ -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<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 };
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue