0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-30 20:33:54 -05:00
logto/packages/core/src/routes/session/continue.ts
2022-11-14 17:57:47 +08:00

178 lines
5.2 KiB
TypeScript

import { passwordRegEx, usernameRegEx } from '@logto/core-kit';
import type { Provider } from 'oidc-provider';
import { object, string } from 'zod';
import RequestError from '@/errors/RequestError';
import { assignInteractionResults, getApplicationIdFromInteraction } from '@/lib/session';
import { getSignInExperienceForApplication } from '@/lib/sign-in-experience';
import { encryptUserPassword } from '@/lib/user';
import koaGuard from '@/middleware/koa-guard';
import {
findUserById,
hasUser,
hasUserWithEmail,
hasUserWithPhone,
updateUserById,
} from '@/queries/user';
import assertThat from '@/utils/assert-that';
import type { AnonymousRouter } from '../types';
import { continueEmailSessionResultGuard, continueSmsSessionResultGuard } from './types';
import {
checkRequiredProfile,
getContinueSignInResult,
getRoutePrefix,
getVerificationStorageFromInteraction,
isUserPasswordSet,
} from './utils';
export const continueRoute = getRoutePrefix('sign-in', 'continue');
export default function continueRoutes<T extends AnonymousRouter>(router: T, provider: Provider) {
router.post(
`${continueRoute}/password`,
koaGuard({
body: object({
password: string().regex(passwordRegEx),
}),
}),
async (ctx, next) => {
const { password } = ctx.guard.body;
const { userId } = await getContinueSignInResult(ctx, provider);
const user = await findUserById(userId);
// Social identities can take place the role of password
assertThat(
!isUserPasswordSet(user),
new RequestError({
code: 'user.password_exists',
})
);
const { passwordEncrypted, passwordEncryptionMethod } = await encryptUserPassword(password);
const updatedUser = await updateUserById(userId, {
passwordEncrypted,
passwordEncryptionMethod,
});
const signInExperience = await getSignInExperienceForApplication(
await getApplicationIdFromInteraction(ctx, provider)
);
await checkRequiredProfile(ctx, provider, updatedUser, signInExperience);
await assignInteractionResults(ctx, provider, { login: { accountId: updatedUser.id } });
return next();
}
);
router.post(
`${continueRoute}/username`,
koaGuard({
body: object({
username: string().regex(usernameRegEx),
}),
}),
async (ctx, next) => {
const { username } = ctx.guard.body;
const { userId } = await getContinueSignInResult(ctx, provider);
const user = await findUserById(userId);
assertThat(
!user.username,
new RequestError({
code: 'user.username_exists',
})
);
assertThat(
!(await hasUser(username)),
new RequestError({
code: 'user.username_exists_register',
status: 422,
})
);
const updatedUser = await updateUserById(userId, {
username,
});
const signInExperience = await getSignInExperienceForApplication(
await getApplicationIdFromInteraction(ctx, provider)
);
await checkRequiredProfile(ctx, provider, updatedUser, signInExperience);
await assignInteractionResults(ctx, provider, { login: { accountId: updatedUser.id } });
return next();
}
);
router.post(`${continueRoute}/email`, async (ctx, next) => {
const { userId } = await getContinueSignInResult(ctx, provider);
const { email } = await getVerificationStorageFromInteraction(
ctx,
provider,
continueEmailSessionResultGuard
);
const user = await findUserById(userId);
assertThat(
!user.primaryEmail,
new RequestError({
code: 'user.email_exists',
})
);
assertThat(
!(await hasUserWithEmail(email)),
new RequestError({
code: 'user.email_exists_register',
status: 422,
})
);
const updatedUser = await updateUserById(userId, {
primaryEmail: email,
});
const signInExperience = await getSignInExperienceForApplication(
await getApplicationIdFromInteraction(ctx, provider)
);
await checkRequiredProfile(ctx, provider, updatedUser, signInExperience);
await assignInteractionResults(ctx, provider, { login: { accountId: updatedUser.id } });
return next();
});
router.post(`${continueRoute}/sms`, async (ctx, next) => {
const { userId } = await getContinueSignInResult(ctx, provider);
const { phone } = await getVerificationStorageFromInteraction(
ctx,
provider,
continueSmsSessionResultGuard
);
const user = await findUserById(userId);
assertThat(
!user.primaryPhone,
new RequestError({
code: 'user.sms_exists',
})
);
assertThat(
!(await hasUserWithPhone(phone)),
new RequestError({
code: 'user.phone_exists_register',
status: 422,
})
);
const updatedUser = await updateUserById(userId, {
primaryPhone: phone,
});
const signInExperience = await getSignInExperienceForApplication(
await getApplicationIdFromInteraction(ctx, provider)
);
await checkRequiredProfile(ctx, provider, updatedUser, signInExperience);
await assignInteractionResults(ctx, provider, { login: { accountId: updatedUser.id } });
return next();
});
}