Skip to content

Commit

Permalink
Merge pull request #419 from aXenDeveloper/remove_bcrypt
Browse files Browse the repository at this point in the history
perf(backend)!: Remove bcrypt, change password hash to crypto
  • Loading branch information
aXenDeveloper authored Jul 19, 2024
2 parents 626672c + 6b9dbdf commit a50a6ae
Show file tree
Hide file tree
Showing 100 changed files with 1,124 additions and 1,086 deletions.
26 changes: 15 additions & 11 deletions apps/docs/content/docs/dev/captcha.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -116,23 +116,27 @@ import {
Core_Sessions__Sign_UpMutation,
Core_Sessions__Sign_UpMutationVariables,
} from '@/graphql/graphql';
import { fetcher } from '@/graphql/fetcher';
import { fetcher, FetcherErrorType } from '@/graphql/fetcher';

interface Args extends Core_Sessions__Sign_UpMutationVariables {
token: string; // [!code highlight]
}

export const mutationApi = async (variables: Args) => {
const data = await fetcher<
Core_Sessions__Sign_UpMutation,
Core_Sessions__Sign_UpMutationVariables
>({
query: Core_Sessions__Sign_Up,
variables,
headers: {
'x-vitnode-captcha-token': variables.token, // [!code highlight]
},
});
try {
const data = await fetcher<
Core_Sessions__Sign_UpMutation,
Core_Sessions__Sign_UpMutationVariables
>({
query: Core_Sessions__Sign_Up,
variables,
headers: {
'x-vitnode-captcha-token': variables.token, // [!code highlight]
},
});
} catch (e) {
return { error: e as FetcherErrorType };
}

revalidatePath('/', 'layout');

Expand Down
20 changes: 13 additions & 7 deletions apps/docs/content/docs/dev/fetch.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,21 @@ import {
Core_Sessions__Sign_OutMutation,
Core_Sessions__Sign_OutMutationVariables,
} from '@/graphql/graphql';
import { fetcher } from 'vitnode-frontend/graphql/fetcher';
import { fetcher, FetcherErrorType } from 'vitnode-frontend/graphql/fetcher';

export const mutationApi = async () => {
await fetcher<
Core_Sessions__Sign_OutMutation, // Mutation type
Core_Sessions__Sign_OutMutationVariables // Variables type
>({
query: Core_Sessions__Sign_Out, // Schema GraphQL mutation
});
try {
const data = await fetcher<
Core_Sessions__Sign_OutMutation, // Mutation type
Core_Sessions__Sign_OutMutationVariables // Variables type
>({
query: Core_Sessions__Sign_Out, // Schema GraphQL mutation
});

return { data };
} catch (e) {
return { error: e as FetcherErrorType };
}
};
```

Expand Down
14 changes: 6 additions & 8 deletions packages/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,16 @@
],
"peerDependencies": {
"@nestjs/apollo": "^12",
"@nestjs/common": "^10.3.10",
"@nestjs/core": "^10.3.10",
"@nestjs/graphql": "^12",
"@react-email/components": "^0.0.21",
"class-validator": "^0.14.1",
"drizzle-kit": "0.22.8",
"drizzle-orm": "0.31.4",
"react": "^19.0.0-rc.0",
"react-dom": "^19.0.0-rc.0",
"typescript": "^5.5.3",
"@nestjs/core": "^10.3.10",
"@nestjs/common": "^10.3.10"
"typescript": "^5.5.3"
},
"dependencies": {
"@apollo/server": "^4.10.4",
Expand All @@ -64,7 +64,6 @@
"@react-email/render": "^0.0.16",
"@swc/cli": "^0.4.0",
"@swc/core": "^1.6.13",
"bcrypt": "^5.1.1",
"busboy": "^1.6.0",
"cookie-parser": "^1.4.6",
"helmet": "^7.1.0",
Expand All @@ -77,11 +76,10 @@
"vitnode-shared": "workspace:*"
},
"devDependencies": {
"@nestjs/graphql": "^12.2.0",
"@nestjs/common": "^10.3.10",
"@nestjs/core": "^10.3.10",
"@nestjs/graphql": "^12.2.0",
"@react-email/components": "^0.0.21",
"@types/bcrypt": "^5.0.2",
"@types/busboy": "^1.5.4",
"@types/cookie-parser": "^1.4.7",
"@types/express": "^4.17.21",
Expand All @@ -91,12 +89,12 @@
"@types/object-path": "^0.11.4",
"@types/pg": "^8.11.6",
"@types/react": "^18.3.3",
"class-validator": "^0.14.1",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
"concurrently": "^8.2.2",
"dotenv": "^16.4.5",
"drizzle-kit": "^0.22.8",
"drizzle-orm": "^0.31.4",
"dotenv": "^16.4.5",
"eslint-config-typescript-vitnode": "workspace:*",
"express": "^4.19.2",
"graphql": "^16.9.0",
Expand Down
2 changes: 0 additions & 2 deletions packages/backend/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,6 @@ const config = () => {
login_token_secret: process.env.LOGIN_TOKEN_SECRET ?? '',
frontend_url: frontend_url.url,
port: parseInt(process.env.PORT, 10) || 8080,
password_salt: 10,
password_reset_salt: 9,
cookies: {
domain:
frontend_url.hostname === 'localhost'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,19 @@
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { eq } from 'drizzle-orm';

import { ChangePasswordCoreMembersArgs } from './dto/change_password.args';

import { DatabaseService } from '@/utils/database/database.service';
import { User } from '@/decorators';
import { encryptPassword } from '@/core/sessions/encrypt_password';
import { encryptPassword } from '@/core/sessions/password';
import {
core_users,
core_users_pass_reset,
} from '@/plugins/core/admin/database/schema/users';

@Injectable()
export class ChangePasswordCoreMembersService {
constructor(
private readonly databaseService: DatabaseService,
private readonly configService: ConfigService,
) {}
constructor(private readonly databaseService: DatabaseService) {}

async change_password({
hashKey,
Expand All @@ -29,7 +25,7 @@ export class ChangePasswordCoreMembersService {
});

const id = keyData.user_id;
const hashPassword = await encryptPassword(this.configService, password);
const hashPassword = await encryptPassword(password);

const update = await this.databaseService.db
.update(core_users)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,19 @@
import { Inject, Injectable } from '@nestjs/common';
import { genSalt, hash } from 'bcrypt';
import { generateRandomString } from 'vitnode-shared';
import { ConfigService } from '@nestjs/config';
// import { generateRandomString } from 'vitnode-shared';

import { CreateKeyResetPasswordCoreMembersArgs } from './dto/create_key.args';
import { ContentCreateKeyEmail } from './_email/content';

import { DatabaseService } from '@/utils/database/database.service';
import { NotFoundError } from '@/errors';
import { core_users_pass_reset } from '@/plugins/core/admin/database/schema/users';
// import { core_users_pass_reset } from '@/plugins/core/admin/database/schema/users';
import { SendAdminEmailService } from '../../../admin/email/send/send.service';
import { EmailHelpersServiceType, getTranslationForEmail } from '@/providers';

@Injectable()
export class CreateKeyResetPasswordCoreMembersService {
constructor(
private readonly databaseService: DatabaseService,
private readonly configService: ConfigService,
private readonly mailService: SendAdminEmailService,
@Inject('EmailHelpersService')
private readonly emailHelpersService: EmailHelpersServiceType,
Expand All @@ -33,16 +30,16 @@ export class CreateKeyResetPasswordCoreMembersService {
throw new NotFoundError('User');
}

const key = generateRandomString(32);
const keySalt = await genSalt(
this.configService.getOrThrow('password_reset_salt'),
);
const hashKey = await hash(key, keySalt);
await this.databaseService.db.insert(core_users_pass_reset).values({
user_id: user.id,
key: hashKey,
expires: new Date(Date.now() + 1000 * 60 * 60), // 1 hour
});
// const key = generateRandomString(32);
// const keySalt = await genSalt(
// this.configService.getOrThrow('password_reset_salt'),
// );
// const hashKey = await hash(key, keySalt);
// await this.databaseService.db.insert(core_users_pass_reset).values({
// user_id: user.id,
// key: hashKey,
// expires: new Date(Date.now() + 1000 * 60 * 60), // 1 hour
// });

const t = getTranslationForEmail('core.reset_password', user.language);

Expand All @@ -52,7 +49,7 @@ export class CreateKeyResetPasswordCoreMembersService {
message: ContentCreateKeyEmail({
language: user.language,
helpers: this.emailHelpersService.getHelpersForEmail(),
key: hashKey,
key: '',
}),
preview_text: t('preview_text'),
user,
Expand Down
14 changes: 0 additions & 14 deletions packages/backend/src/core/sessions/encrypt_password.ts

This file was deleted.

28 changes: 28 additions & 0 deletions packages/backend/src/core/sessions/password.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import crypto from 'crypto';

async function encryptPassword(password: string): Promise<string> {
return new Promise((resolve, reject) => {
const salt = crypto.randomBytes(8).toString('hex');

crypto.scrypt(password, salt, 64, (err, derivedKey) => {
if (err) reject(err);

resolve(salt + ':' + derivedKey.toString('hex'));
});
});
}

async function verifyPassword(
password: string,
hash: string,
): Promise<boolean> {
return new Promise((resolve, reject) => {
const [salt, key] = hash.split(':');
crypto.scrypt(password, salt, 64, (err, derivedKey) => {
if (err) reject(err);
resolve(key == derivedKey.toString('hex'));
});
});
}

export { encryptPassword, verifyPassword };
4 changes: 2 additions & 2 deletions packages/backend/src/core/sessions/sign_in/sign_in.service.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { Injectable } from '@nestjs/common';
import { compare } from 'bcrypt';
import { JwtService } from '@nestjs/jwt';
import { ConfigService } from '@nestjs/config';
import { and, eq } from 'drizzle-orm';

import { SignInCoreSessionsArgs } from './dto/sign_in.args';
import { DeviceSignInCoreSessionsService } from './device.service';
import { verifyPassword } from '../password';

import { GqlContext } from '../../../utils';
import { DatabaseService } from '@/utils/database/database.service';
Expand Down Expand Up @@ -182,7 +182,7 @@ export class SignInCoreSessionsService {
});
if (!user) throw new AccessDeniedError();

const validPassword = await compare(password, user.password);
const validPassword = await verifyPassword(password, user.password);
if (!validPassword) throw new AccessDeniedError();

// If admin mode is enabled, check if user has access to admin cp
Expand Down
4 changes: 2 additions & 2 deletions packages/backend/src/core/sessions/sign_up/sign_up.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { removeSpecialCharacters } from 'vitnode-shared';
import { SignUpCoreSessionsArgs } from './dto/sign_up.args';
import { SignUpCoreSessionsObj } from './dto/sign_up.obj';
import { AvatarColorService } from './helpers/avatar-color.service';
import { encryptPassword } from '../encrypt_password';
import { encryptPassword } from '../password';

import { CaptchaCoreCaptchaSecurityService } from '@/core/admin/security/captcha/captcha.service';
import { DatabaseService } from '@/utils/database/database.service';
Expand Down Expand Up @@ -83,7 +83,7 @@ export class SignUpCoreSessionsService extends AvatarColorService {
});
}

const hashPassword = await encryptPassword(this.configService, password);
const hashPassword = await encryptPassword(password);

const user = await this.databaseService.db
.insert(core_users)
Expand Down
2 changes: 1 addition & 1 deletion packages/frontend/src/components/ui/form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import {
useFormContext,
} from 'react-hook-form';
import { useTranslations } from 'next-intl';
import { useBeforeUnload } from '@/helpers/use-before-unload';

import { useBeforeUnload } from '@/helpers/use-before-unload';
import { Label } from './label';
import { useDialog } from './dialog';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use server';

import { revalidatePath } from 'next/cache';
import { fetcher } from 'vitnode-frontend/graphql/fetcher';
import { fetcher, FetcherErrorType } from 'vitnode-frontend/graphql/fetcher';

import {
Core_Editor_Files__Upload,
Expand All @@ -14,24 +14,28 @@ export const uploadMutationApi = async (formData: FormData) => {
const plugin = formData.get('plugin') as string;
const folder = formData.get('folder') as string;

const data = await fetcher<
Core_Editor_Files__UploadMutation,
Omit<Core_Editor_Files__UploadMutationVariables, 'file'>
>({
query: Core_Editor_Files__Upload,
variables: {
plugin,
folder,
},
uploads: [
{
files: file,
variable: 'file',
try {
const data = await fetcher<
Core_Editor_Files__UploadMutation,
Omit<Core_Editor_Files__UploadMutationVariables, 'file'>
>({
query: Core_Editor_Files__Upload,
variables: {
plugin,
folder,
},
],
});
uploads: [
{
files: file,
variable: 'file',
},
],
});

revalidatePath('/settings/files', 'page');
revalidatePath('/settings/files', 'page');

return data;
return { data };
} catch (e) {
return { error: e as FetcherErrorType };
}
};
Loading

0 comments on commit a50a6ae

Please sign in to comment.