mirror of
https://github.com/logto-io/logto.git
synced 2024-12-16 20:26:19 -05:00
refactor(core): encryptUserPassword (#135)
This commit is contained in:
parent
7d7b8112f6
commit
3480d05366
4 changed files with 45 additions and 23 deletions
|
@ -1,6 +1,8 @@
|
|||
import { PasswordEncryptionMethod } from '@logto/schemas';
|
||||
|
||||
import { hasUserWithId } from '@/queries/user';
|
||||
|
||||
import { generateUserId } from './user';
|
||||
import { encryptUserPassword, generateUserId } from './user';
|
||||
|
||||
jest.mock('@/queries/user');
|
||||
|
||||
|
@ -48,3 +50,13 @@ describe('generateUserId()', () => {
|
|||
expect(mockedHasUserWithId).toBeCalledTimes(11);
|
||||
});
|
||||
});
|
||||
|
||||
describe('encryptUserPassword()', () => {
|
||||
it('generates salt, encrypted and method', () => {
|
||||
const { passwordEncryptionMethod, passwordEncrypted, passwordEncryptionSalt } =
|
||||
encryptUserPassword('user-id', 'password');
|
||||
expect(passwordEncryptionMethod).toEqual(PasswordEncryptionMethod.SaltAndPepper);
|
||||
expect(passwordEncrypted).toHaveLength(64);
|
||||
expect(passwordEncryptionSalt).toHaveLength(21);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
import { PasswordEncryptionMethod } from '@logto/schemas';
|
||||
import { nanoid } from 'nanoid';
|
||||
import pRetry from 'p-retry';
|
||||
|
||||
import { hasUserWithId } from '@/queries/user';
|
||||
import { buildIdGenerator } from '@/utils/id';
|
||||
import { encryptPassword } from '@/utils/password';
|
||||
|
||||
const userId = buildIdGenerator(12);
|
||||
|
||||
|
@ -18,3 +21,23 @@ export const generateUserId = async (retries = 500) =>
|
|||
},
|
||||
{ retries, factor: 0 } // No need for exponential backoff
|
||||
);
|
||||
|
||||
export const encryptUserPassword = (
|
||||
userId: string,
|
||||
password: string
|
||||
): {
|
||||
passwordEncryptionSalt: string;
|
||||
passwordEncrypted: string;
|
||||
passwordEncryptionMethod: PasswordEncryptionMethod;
|
||||
} => {
|
||||
const passwordEncryptionSalt = nanoid();
|
||||
const passwordEncryptionMethod = PasswordEncryptionMethod.SaltAndPepper;
|
||||
const passwordEncrypted = encryptPassword(
|
||||
userId,
|
||||
password,
|
||||
passwordEncryptionSalt,
|
||||
passwordEncryptionMethod
|
||||
);
|
||||
|
||||
return { passwordEncrypted, passwordEncryptionMethod, passwordEncryptionSalt };
|
||||
};
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
import { PasswordEncryptionMethod, userInfoSelectFields } from '@logto/schemas';
|
||||
import { userInfoSelectFields } from '@logto/schemas';
|
||||
import pick from 'lodash.pick';
|
||||
import { nanoid } from 'nanoid';
|
||||
import { Provider } from 'oidc-provider';
|
||||
import { object, string } from 'zod';
|
||||
|
||||
import RequestError from '@/errors/RequestError';
|
||||
import { generateUserId } from '@/lib/user';
|
||||
import { encryptUserPassword, generateUserId } from '@/lib/user';
|
||||
import koaGuard from '@/middleware/koa-guard';
|
||||
import {
|
||||
deleteUserById,
|
||||
|
@ -15,7 +14,6 @@ import {
|
|||
insertUser,
|
||||
updateUserById,
|
||||
} from '@/queries/user';
|
||||
import { encryptPassword } from '@/utils/password';
|
||||
|
||||
import { AnonymousRouter } from './types';
|
||||
|
||||
|
@ -36,14 +34,9 @@ export default function userRoutes<T extends AnonymousRouter>(router: T, provide
|
|||
}
|
||||
|
||||
const id = await generateUserId();
|
||||
const passwordEncryptionSalt = nanoid();
|
||||
const passwordEncryptionMethod = PasswordEncryptionMethod.SaltAndPepper;
|
||||
const passwordEncrypted = encryptPassword(
|
||||
id,
|
||||
password,
|
||||
passwordEncryptionSalt,
|
||||
passwordEncryptionMethod
|
||||
);
|
||||
|
||||
const { passwordEncryptionSalt, passwordEncrypted, passwordEncryptionMethod } =
|
||||
encryptUserPassword(id, password);
|
||||
|
||||
await insertUser({
|
||||
id,
|
||||
|
@ -102,14 +95,7 @@ export default function userRoutes<T extends AnonymousRouter>(router: T, provide
|
|||
|
||||
await findUserById(userId);
|
||||
|
||||
const passwordEncryptionSalt = nanoid();
|
||||
const passwordEncryptionMethod = PasswordEncryptionMethod.SaltAndPepper;
|
||||
const passwordEncrypted = encryptPassword(
|
||||
userId,
|
||||
password,
|
||||
passwordEncryptionSalt,
|
||||
passwordEncryptionMethod
|
||||
);
|
||||
const { passwordEncrypted, passwordEncryptionSalt } = encryptUserPassword(userId, password);
|
||||
|
||||
await updateUserById(userId, {
|
||||
passwordEncryptionSalt,
|
||||
|
|
|
@ -2,16 +2,17 @@ import { createHash } from 'crypto';
|
|||
|
||||
import { PasswordEncryptionMethod } from '@logto/schemas';
|
||||
import { assertEnv, repeat } from '@silverhand/essentials';
|
||||
import { nanoid } from 'nanoid';
|
||||
import { number, string } from 'zod';
|
||||
|
||||
import assertThat from '@/utils/assert-that';
|
||||
|
||||
const peppers = string()
|
||||
.array()
|
||||
.parse(JSON.parse(assertEnv('PASSWORD_PEPPERS')));
|
||||
.parse(process.env.NODE_ENV === 'test' ? [nanoid()] : JSON.parse(assertEnv('PASSWORD_PEPPERS')));
|
||||
const iterationCount = number()
|
||||
.min(100)
|
||||
.parse(Number(assertEnv('PASSWORD_INTERATION_COUNT')));
|
||||
.parse(process.env.NODE_ENV === 'test' ? 1000 : Number(assertEnv('PASSWORD_INTERATION_COUNT')));
|
||||
|
||||
export const encryptPassword = (
|
||||
id: string,
|
||||
|
|
Loading…
Reference in a new issue