mirror of
https://github.com/logto-io/logto.git
synced 2024-12-30 20:33:54 -05:00
revert(core): manually revert forgot password logic (#365)
* Revert "feat(core): add email forgot password flow (send/verify passcode) (#336)" * Revert "feat(core): post /session/forgot-password/phone/verify-passcode-and-reset-password (#334)" * Revert "feat(core): add forgot password send a passcode to phone route (#326)"
This commit is contained in:
parent
e8a50039c6
commit
9ed146645b
8 changed files with 16 additions and 374 deletions
|
@ -1,22 +1,20 @@
|
||||||
import { UsersPasswordEncryptionMethod } from '@logto/schemas';
|
import { UsersPasswordEncryptionMethod } from '@logto/schemas';
|
||||||
|
|
||||||
import { hasUserWithId, findUserById } from '@/queries/user';
|
import { hasUserWithId } from '@/queries/user';
|
||||||
|
|
||||||
import { encryptUserPassword, generateUserId, findUserSignInMethodsById } from './user';
|
import { encryptUserPassword, generateUserId } from './user';
|
||||||
|
|
||||||
jest.mock('@/queries/user', () => ({
|
jest.mock('@/queries/user');
|
||||||
findUserById: jest.fn(),
|
|
||||||
hasUserWithId: jest.fn(),
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('generateUserId()', () => {
|
describe('generateUserId()', () => {
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
jest.clearAllMocks();
|
(hasUserWithId as jest.MockedFunction<typeof hasUserWithId>).mockClear();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('generates user ID with correct length when no conflict found', async () => {
|
it('generates user ID with correct length when no conflict found', async () => {
|
||||||
const mockedHasUserWithId = hasUserWithId as jest.Mock;
|
const mockedHasUserWithId = (
|
||||||
mockedHasUserWithId.mockImplementationOnce(async () => false);
|
hasUserWithId as jest.MockedFunction<typeof hasUserWithId>
|
||||||
|
).mockImplementationOnce(async () => false);
|
||||||
|
|
||||||
await expect(generateUserId()).resolves.toHaveLength(12);
|
await expect(generateUserId()).resolves.toHaveLength(12);
|
||||||
expect(mockedHasUserWithId).toBeCalledTimes(1);
|
expect(mockedHasUserWithId).toBeCalledTimes(1);
|
||||||
|
@ -25,8 +23,9 @@ describe('generateUserId()', () => {
|
||||||
it('generates user ID with correct length when retry limit is not reached', async () => {
|
it('generates user ID with correct length when retry limit is not reached', async () => {
|
||||||
// eslint-disable-next-line @silverhand/fp/no-let
|
// eslint-disable-next-line @silverhand/fp/no-let
|
||||||
let tried = 0;
|
let tried = 0;
|
||||||
const mockedHasUserWithId = hasUserWithId as jest.Mock;
|
const mockedHasUserWithId = (
|
||||||
mockedHasUserWithId.mockImplementation(async () => {
|
hasUserWithId as jest.MockedFunction<typeof hasUserWithId>
|
||||||
|
).mockImplementation(async () => {
|
||||||
if (tried) {
|
if (tried) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -42,8 +41,9 @@ describe('generateUserId()', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('rejects with correct error message when retry limit is reached', async () => {
|
it('rejects with correct error message when retry limit is reached', async () => {
|
||||||
const mockedHasUserWithId = hasUserWithId as jest.Mock;
|
const mockedHasUserWithId = (
|
||||||
mockedHasUserWithId.mockImplementation(async () => true);
|
hasUserWithId as jest.MockedFunction<typeof hasUserWithId>
|
||||||
|
).mockImplementation(async () => true);
|
||||||
|
|
||||||
await expect(generateUserId(10)).rejects.toThrow(
|
await expect(generateUserId(10)).rejects.toThrow(
|
||||||
'Cannot generate user ID in reasonable retries'
|
'Cannot generate user ID in reasonable retries'
|
||||||
|
@ -61,78 +61,3 @@ describe('encryptUserPassword()', () => {
|
||||||
expect(passwordEncryptionSalt).toHaveLength(21);
|
expect(passwordEncryptionSalt).toHaveLength(21);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('findUserSignInMethodsById()', () => {
|
|
||||||
afterEach(() => {
|
|
||||||
jest.clearAllMocks();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('generate and test user with username and password sign-in method', async () => {
|
|
||||||
const mockFindUserById = findUserById as jest.Mock;
|
|
||||||
mockFindUserById.mockResolvedValue({
|
|
||||||
username: 'abcd',
|
|
||||||
passwordEncrypted: '1234567890',
|
|
||||||
passwordEncryptionMethod: UsersPasswordEncryptionMethod.SaltAndPepper,
|
|
||||||
passwordEncryptionSalt: '123456790',
|
|
||||||
});
|
|
||||||
const { usernameAndPassword, emailPasswordless, phonePasswordless, social } =
|
|
||||||
await findUserSignInMethodsById('');
|
|
||||||
expect(usernameAndPassword).toEqual(true);
|
|
||||||
expect(emailPasswordless).toBeFalsy();
|
|
||||||
expect(phonePasswordless).toBeFalsy();
|
|
||||||
expect(social).toBeFalsy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('generate and test user with email passwordless sign-in method', async () => {
|
|
||||||
const mockFindUserById = findUserById as jest.Mock;
|
|
||||||
mockFindUserById.mockResolvedValue({
|
|
||||||
primaryEmail: 'b@a.com',
|
|
||||||
identities: {},
|
|
||||||
});
|
|
||||||
const { usernameAndPassword, emailPasswordless, phonePasswordless, social } =
|
|
||||||
await findUserSignInMethodsById('');
|
|
||||||
expect(usernameAndPassword).toBeFalsy();
|
|
||||||
expect(emailPasswordless).toEqual(true);
|
|
||||||
expect(phonePasswordless).toBeFalsy();
|
|
||||||
expect(social).toBeFalsy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('generate and test user with phone passwordless sign-in method', async () => {
|
|
||||||
const mockFindUserById = findUserById as jest.Mock;
|
|
||||||
mockFindUserById.mockResolvedValue({
|
|
||||||
primaryPhone: '13000000000',
|
|
||||||
});
|
|
||||||
const { usernameAndPassword, emailPasswordless, phonePasswordless, social } =
|
|
||||||
await findUserSignInMethodsById('');
|
|
||||||
expect(usernameAndPassword).toBeFalsy();
|
|
||||||
expect(emailPasswordless).toBeFalsy();
|
|
||||||
expect(phonePasswordless).toEqual(true);
|
|
||||||
expect(social).toBeFalsy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('generate and test user with social sign-in method (single social connector information in record)', async () => {
|
|
||||||
const mockFindUserById = findUserById as jest.Mock;
|
|
||||||
mockFindUserById.mockResolvedValue({
|
|
||||||
identities: { connector1: { userId: 'foo1' } },
|
|
||||||
});
|
|
||||||
const { usernameAndPassword, emailPasswordless, phonePasswordless, social } =
|
|
||||||
await findUserSignInMethodsById('');
|
|
||||||
expect(usernameAndPassword).toBeFalsy();
|
|
||||||
expect(emailPasswordless).toBeFalsy();
|
|
||||||
expect(phonePasswordless).toBeFalsy();
|
|
||||||
expect(social).toEqual(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('generate and test user with social sign-in method (multiple social connectors information in record)', async () => {
|
|
||||||
const mockFindUserById = findUserById as jest.Mock;
|
|
||||||
mockFindUserById.mockResolvedValue({
|
|
||||||
identities: { connector1: { userId: 'foo1' }, connector2: { userId: 'foo2' } },
|
|
||||||
});
|
|
||||||
const { usernameAndPassword, emailPasswordless, phonePasswordless, social } =
|
|
||||||
await findUserSignInMethodsById('');
|
|
||||||
expect(usernameAndPassword).toBeFalsy();
|
|
||||||
expect(emailPasswordless).toBeFalsy();
|
|
||||||
expect(phonePasswordless).toBeFalsy();
|
|
||||||
expect(social).toEqual(true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { UsersPasswordEncryptionMethod, User } from '@logto/schemas';
|
||||||
import { nanoid } from 'nanoid';
|
import { nanoid } from 'nanoid';
|
||||||
import pRetry from 'p-retry';
|
import pRetry from 'p-retry';
|
||||||
|
|
||||||
import { findUserById, findUserByUsername, hasUserWithId } from '@/queries/user';
|
import { findUserByUsername, hasUserWithId } from '@/queries/user';
|
||||||
import assertThat from '@/utils/assert-that';
|
import assertThat from '@/utils/assert-that';
|
||||||
import { buildIdGenerator } from '@/utils/id';
|
import { buildIdGenerator } from '@/utils/id';
|
||||||
import { encryptPassword } from '@/utils/password';
|
import { encryptPassword } from '@/utils/password';
|
||||||
|
@ -43,36 +43,6 @@ export const encryptUserPassword = (
|
||||||
return { passwordEncrypted, passwordEncryptionMethod, passwordEncryptionSalt };
|
return { passwordEncrypted, passwordEncryptionMethod, passwordEncryptionSalt };
|
||||||
};
|
};
|
||||||
|
|
||||||
export const findUserSignInMethodsById = async (
|
|
||||||
id: string
|
|
||||||
): Promise<{
|
|
||||||
usernameAndPassword: boolean;
|
|
||||||
emailPasswordless: boolean;
|
|
||||||
phonePasswordless: boolean;
|
|
||||||
social: boolean;
|
|
||||||
}> => {
|
|
||||||
const user = await findUserById(id);
|
|
||||||
const {
|
|
||||||
username,
|
|
||||||
passwordEncrypted,
|
|
||||||
passwordEncryptionMethod,
|
|
||||||
passwordEncryptionSalt,
|
|
||||||
primaryEmail,
|
|
||||||
primaryPhone,
|
|
||||||
identities,
|
|
||||||
} = user;
|
|
||||||
|
|
||||||
const usernameAndPassword = Boolean(
|
|
||||||
username && passwordEncrypted && passwordEncryptionMethod && passwordEncryptionSalt
|
|
||||||
);
|
|
||||||
const emailPasswordless = Boolean(primaryEmail);
|
|
||||||
const phonePasswordless = Boolean(primaryPhone);
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
||||||
const social = identities && Object.keys(identities).length > 0;
|
|
||||||
|
|
||||||
return { usernameAndPassword, emailPasswordless, phonePasswordless, social };
|
|
||||||
};
|
|
||||||
|
|
||||||
export const findUserByUsernameAndPassword = async (
|
export const findUserByUsernameAndPassword = async (
|
||||||
username: string,
|
username: string,
|
||||||
password: string
|
password: string
|
||||||
|
|
|
@ -729,132 +729,6 @@ describe('sessionRoutes', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('POST /session/forgot-password/phone/send-passcode', () => {
|
|
||||||
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/send-passcode')
|
|
||||||
.send({ phone: '13000000001' });
|
|
||||||
expect(response).toHaveProperty('statusCode', 422);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('create and send passcode', async () => {
|
|
||||||
const response = await sessionRequest
|
|
||||||
.post('/session/forgot-password/phone/send-passcode')
|
|
||||||
.send({ phone: '13000000000' });
|
|
||||||
expect(response.statusCode).toEqual(204);
|
|
||||||
expect(sendPasscode).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
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/forgot-password/email/send-passcode', () => {
|
|
||||||
beforeAll(() => {
|
|
||||||
interactionDetails.mockResolvedValueOnce({
|
|
||||||
jti: 'jti',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('throw if no user can be found with email', async () => {
|
|
||||||
const response = await sessionRequest
|
|
||||||
.post('/session/forgot-password/email/send-passcode')
|
|
||||||
.send({ email: 'b@a.com' });
|
|
||||||
expect(response).toHaveProperty('statusCode', 422);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('create and send passcode', async () => {
|
|
||||||
const response = await sessionRequest
|
|
||||||
.post('/session/forgot-password/email/send-passcode')
|
|
||||||
.send({ email: 'a@a.com' });
|
|
||||||
expect(response.statusCode).toEqual(204);
|
|
||||||
expect(sendPasscode).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('POST /session/forgot-password/email/verify-passcode-and-reset-password', () => {
|
|
||||||
beforeAll(() => {
|
|
||||||
interactionDetails.mockResolvedValueOnce({
|
|
||||||
jti: 'jti',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('throw if no user can be found with email', async () => {
|
|
||||||
const response = await sessionRequest
|
|
||||||
.post('/session/forgot-password/email/verify-passcode-and-reset-password')
|
|
||||||
.send({ email: 'b@a.com', code: '1234', password: '123456' });
|
|
||||||
expect(response).toHaveProperty('statusCode', 422);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('fail to verify passcode', async () => {
|
|
||||||
const response = await sessionRequest
|
|
||||||
.post('/session/forgot-password/email/verify-passcode-and-reset-password')
|
|
||||||
.send({ email: 'a@a.com', 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/email/verify-passcode-and-reset-password')
|
|
||||||
.send({ email: 'a@a.com', 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', () => {
|
describe('POST /session/bind-social', () => {
|
||||||
it('throw if session is not authorized', async () => {
|
it('throw if session is not authorized', async () => {
|
||||||
interactionDetails.mockResolvedValueOnce({});
|
interactionDetails.mockResolvedValueOnce({});
|
||||||
|
|
|
@ -16,7 +16,7 @@ import {
|
||||||
getUserInfoByAuthCode,
|
getUserInfoByAuthCode,
|
||||||
getUserInfoFromInteractionResult,
|
getUserInfoFromInteractionResult,
|
||||||
} from '@/lib/social';
|
} from '@/lib/social';
|
||||||
import { generateUserId, encryptUserPassword, findUserByUsernameAndPassword } from '@/lib/user';
|
import { encryptUserPassword, generateUserId, findUserByUsernameAndPassword } from '@/lib/user';
|
||||||
import koaGuard from '@/middleware/koa-guard';
|
import koaGuard from '@/middleware/koa-guard';
|
||||||
import {
|
import {
|
||||||
hasUserWithEmail,
|
hasUserWithEmail,
|
||||||
|
@ -475,128 +475,6 @@ export default function sessionRoutes<T extends AnonymousRouter>(router: T, prov
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
router.post(
|
|
||||||
'/session/forgot-password/phone/send-passcode',
|
|
||||||
koaGuard({ body: object({ phone: string().regex(phoneRegEx) }) }),
|
|
||||||
async (ctx, next) => {
|
|
||||||
const { jti } = await provider.interactionDetails(ctx.req, ctx.res);
|
|
||||||
const { phone } = 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 })
|
|
||||||
);
|
|
||||||
const { id } = await findUserByPhone(phone);
|
|
||||||
ctx.userLog.userId = id;
|
|
||||||
|
|
||||||
const passcode = await createPasscode(jti, PasscodeType.ForgotPassword, { phone });
|
|
||||||
await sendPasscode(passcode);
|
|
||||||
ctx.status = 204;
|
|
||||||
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
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/forgot-password/email/send-passcode',
|
|
||||||
koaGuard({ body: object({ email: string().regex(emailRegEx) }) }),
|
|
||||||
async (ctx, next) => {
|
|
||||||
const { jti } = await provider.interactionDetails(ctx.req, ctx.res);
|
|
||||||
const { email } = ctx.guard.body;
|
|
||||||
ctx.userLog.email = email;
|
|
||||||
ctx.userLog.type = UserLogType.ForgotPasswordEmail;
|
|
||||||
|
|
||||||
assertThat(
|
|
||||||
await hasUserWithEmail(email),
|
|
||||||
new RequestError({ code: 'user.email_not_exists', status: 422 })
|
|
||||||
);
|
|
||||||
const { id } = await findUserByEmail(email);
|
|
||||||
ctx.userLog.userId = id;
|
|
||||||
|
|
||||||
const passcode = await createPasscode(jti, PasscodeType.ForgotPassword, { email });
|
|
||||||
await sendPasscode(passcode);
|
|
||||||
ctx.status = 204;
|
|
||||||
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
router.post(
|
|
||||||
'/session/forgot-password/email/verify-passcode-and-reset-password',
|
|
||||||
koaGuard({
|
|
||||||
body: object({
|
|
||||||
email: string().regex(emailRegEx),
|
|
||||||
code: string(),
|
|
||||||
password: string().regex(passwordRegEx),
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
async (ctx, next) => {
|
|
||||||
const { jti } = await provider.interactionDetails(ctx.req, ctx.res);
|
|
||||||
const { email, code, password } = ctx.guard.body;
|
|
||||||
ctx.userLog.email = email;
|
|
||||||
ctx.userLog.type = UserLogType.ForgotPasswordEmail;
|
|
||||||
|
|
||||||
assertThat(
|
|
||||||
await hasUserWithEmail(email),
|
|
||||||
new RequestError({ code: 'user.email_not_exists', status: 422 })
|
|
||||||
);
|
|
||||||
|
|
||||||
await verifyPasscode(jti, PasscodeType.ForgotPassword, code, { email });
|
|
||||||
const { id } = await findUserByEmail(email);
|
|
||||||
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(
|
router.post(
|
||||||
'/session/bind-social',
|
'/session/bind-social',
|
||||||
koaGuard({
|
koaGuard({
|
||||||
|
|
|
@ -161,8 +161,6 @@ const errors = {
|
||||||
phone_not_exists: 'The phone number has not been registered yet.',
|
phone_not_exists: 'The phone number has not been registered yet.',
|
||||||
identity_not_exists: 'The social account has not been registered yet.',
|
identity_not_exists: 'The social account has not been registered yet.',
|
||||||
identity_exists: 'The social account has been registered.',
|
identity_exists: 'The social account has been registered.',
|
||||||
username_password_signin_not_exists:
|
|
||||||
'Signing in with username and password has not been enabled for this user.',
|
|
||||||
},
|
},
|
||||||
password: {
|
password: {
|
||||||
unsupported_encryption_method: 'The encryption method {{name}} is not supported.',
|
unsupported_encryption_method: 'The encryption method {{name}} is not supported.',
|
||||||
|
|
|
@ -162,7 +162,6 @@ const errors = {
|
||||||
phone_not_exists: '手机号码尚未注册。',
|
phone_not_exists: '手机号码尚未注册。',
|
||||||
identity_not_exists: '该社交账号尚未注册。',
|
identity_not_exists: '该社交账号尚未注册。',
|
||||||
identity_exists: '该社交账号已被注册。',
|
identity_exists: '该社交账号已被注册。',
|
||||||
username_password_signin_not_exists: '该账号暂未开通账号密码登录方式。',
|
|
||||||
},
|
},
|
||||||
password: {
|
password: {
|
||||||
unsupported_encryption_method: '不支持的加密方法 {{name}}。',
|
unsupported_encryption_method: '不支持的加密方法 {{name}}。',
|
||||||
|
|
|
@ -19,8 +19,6 @@ export enum UserLogType {
|
||||||
RegisterEmail = 'RegisterEmail',
|
RegisterEmail = 'RegisterEmail',
|
||||||
RegisterPhone = 'RegisterPhone',
|
RegisterPhone = 'RegisterPhone',
|
||||||
RegisterSocial = 'RegisterSocial',
|
RegisterSocial = 'RegisterSocial',
|
||||||
ForgotPasswordEmail = 'ForgotPasswordEmail',
|
|
||||||
ForgotPasswordPhone = 'ForgotPasswordPhone',
|
|
||||||
ExchangeAccessToken = 'ExchangeAccessToken',
|
ExchangeAccessToken = 'ExchangeAccessToken',
|
||||||
}
|
}
|
||||||
export enum UserLogResult {
|
export enum UserLogResult {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
create type user_log_type as enum ('SignInUsernameAndPassword', 'SignInEmail', 'SignInPhone', 'SignInSocial', 'RegisterUsernameAndPassword', 'RegisterEmail', 'RegisterPhone', 'RegisterSocial', 'ForgotPasswordEmail', 'ForgotPasswordPhone', 'ExchangeAccessToken');
|
create type user_log_type as enum ('SignInUsernameAndPassword', 'SignInEmail', 'SignInPhone', 'SignInSocial', 'RegisterUsernameAndPassword', 'RegisterEmail', 'RegisterPhone', 'RegisterSocial', 'ExchangeAccessToken');
|
||||||
|
|
||||||
create type user_log_result as enum ('Success', 'Failed');
|
create type user_log_result as enum ('Success', 'Failed');
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue