0
Fork 0
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:
Wang Sijie 2021-11-18 16:29:24 +08:00 committed by GitHub
parent 7d7b8112f6
commit 3480d05366
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 45 additions and 23 deletions

View file

@ -1,6 +1,8 @@
import { PasswordEncryptionMethod } from '@logto/schemas';
import { hasUserWithId } from '@/queries/user'; import { hasUserWithId } from '@/queries/user';
import { generateUserId } from './user'; import { encryptUserPassword, generateUserId } from './user';
jest.mock('@/queries/user'); jest.mock('@/queries/user');
@ -48,3 +50,13 @@ describe('generateUserId()', () => {
expect(mockedHasUserWithId).toBeCalledTimes(11); 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);
});
});

View file

@ -1,7 +1,10 @@
import { PasswordEncryptionMethod } from '@logto/schemas';
import { nanoid } from 'nanoid';
import pRetry from 'p-retry'; import pRetry from 'p-retry';
import { hasUserWithId } from '@/queries/user'; import { hasUserWithId } from '@/queries/user';
import { buildIdGenerator } from '@/utils/id'; import { buildIdGenerator } from '@/utils/id';
import { encryptPassword } from '@/utils/password';
const userId = buildIdGenerator(12); const userId = buildIdGenerator(12);
@ -18,3 +21,23 @@ export const generateUserId = async (retries = 500) =>
}, },
{ retries, factor: 0 } // No need for exponential backoff { 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 };
};

View file

@ -1,11 +1,10 @@
import { PasswordEncryptionMethod, userInfoSelectFields } from '@logto/schemas'; import { userInfoSelectFields } from '@logto/schemas';
import pick from 'lodash.pick'; import pick from 'lodash.pick';
import { nanoid } from 'nanoid';
import { Provider } from 'oidc-provider'; import { Provider } from 'oidc-provider';
import { object, string } from 'zod'; import { object, string } from 'zod';
import RequestError from '@/errors/RequestError'; import RequestError from '@/errors/RequestError';
import { generateUserId } from '@/lib/user'; import { encryptUserPassword, generateUserId } from '@/lib/user';
import koaGuard from '@/middleware/koa-guard'; import koaGuard from '@/middleware/koa-guard';
import { import {
deleteUserById, deleteUserById,
@ -15,7 +14,6 @@ import {
insertUser, insertUser,
updateUserById, updateUserById,
} from '@/queries/user'; } from '@/queries/user';
import { encryptPassword } from '@/utils/password';
import { AnonymousRouter } from './types'; import { AnonymousRouter } from './types';
@ -36,14 +34,9 @@ export default function userRoutes<T extends AnonymousRouter>(router: T, provide
} }
const id = await generateUserId(); const id = await generateUserId();
const passwordEncryptionSalt = nanoid();
const passwordEncryptionMethod = PasswordEncryptionMethod.SaltAndPepper; const { passwordEncryptionSalt, passwordEncrypted, passwordEncryptionMethod } =
const passwordEncrypted = encryptPassword( encryptUserPassword(id, password);
id,
password,
passwordEncryptionSalt,
passwordEncryptionMethod
);
await insertUser({ await insertUser({
id, id,
@ -102,14 +95,7 @@ export default function userRoutes<T extends AnonymousRouter>(router: T, provide
await findUserById(userId); await findUserById(userId);
const passwordEncryptionSalt = nanoid(); const { passwordEncrypted, passwordEncryptionSalt } = encryptUserPassword(userId, password);
const passwordEncryptionMethod = PasswordEncryptionMethod.SaltAndPepper;
const passwordEncrypted = encryptPassword(
userId,
password,
passwordEncryptionSalt,
passwordEncryptionMethod
);
await updateUserById(userId, { await updateUserById(userId, {
passwordEncryptionSalt, passwordEncryptionSalt,

View file

@ -2,16 +2,17 @@ import { createHash } from 'crypto';
import { PasswordEncryptionMethod } from '@logto/schemas'; import { PasswordEncryptionMethod } from '@logto/schemas';
import { assertEnv, repeat } from '@silverhand/essentials'; import { assertEnv, repeat } from '@silverhand/essentials';
import { nanoid } from 'nanoid';
import { number, string } from 'zod'; import { number, string } from 'zod';
import assertThat from '@/utils/assert-that'; import assertThat from '@/utils/assert-that';
const peppers = string() const peppers = string()
.array() .array()
.parse(JSON.parse(assertEnv('PASSWORD_PEPPERS'))); .parse(process.env.NODE_ENV === 'test' ? [nanoid()] : JSON.parse(assertEnv('PASSWORD_PEPPERS')));
const iterationCount = number() const iterationCount = number()
.min(100) .min(100)
.parse(Number(assertEnv('PASSWORD_INTERATION_COUNT'))); .parse(process.env.NODE_ENV === 'test' ? 1000 : Number(assertEnv('PASSWORD_INTERATION_COUNT')));
export const encryptPassword = ( export const encryptPassword = (
id: string, id: string,