server: refactor password hashing & comparison to module
For easier replacement should the hash algorithm ever be changed.
This commit is contained in:
parent
c2372315f7
commit
114d416de0
16 changed files with 46 additions and 84 deletions
10
packages/backend/src/misc/password.ts
Normal file
10
packages/backend/src/misc/password.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
import bcrypt from 'bcryptjs';
|
||||
|
||||
export async function hashPassword(password: string): Promise<string> {
|
||||
const salt = await bcrypt.genSalt(8);
|
||||
return await bcrypt.hash(password, salt);
|
||||
}
|
||||
|
||||
export async function comparePassword(password: string, hash: string): Promise<boolean> {
|
||||
return await bcrypt.compare(password, hash);
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
import { generateKeyPair } from 'node:crypto';
|
||||
import bcrypt from 'bcryptjs';
|
||||
import { IsNull } from 'typeorm';
|
||||
import { User } from '@/models/entities/user.js';
|
||||
import { Users, UsedUsernames } from '@/models/index.js';
|
||||
import { UserProfile } from '@/models/entities/user-profile.js';
|
||||
import { genId } from '@/misc/gen-id.js';
|
||||
import { toPunyNullable } from '@/misc/convert-host.js';
|
||||
import { hashPassword } from '@/misc/password.js';
|
||||
import { UserKeypair } from '@/models/entities/user-keypair.js';
|
||||
import { usersChart } from '@/services/chart/index.js';
|
||||
import { UsedUsername } from '@/models/entities/used-username.js';
|
||||
|
@ -33,9 +33,7 @@ export async function signup(opts: {
|
|||
throw new ApiError('INVALID_PASSWORD');
|
||||
}
|
||||
|
||||
// Generate hash of password
|
||||
const salt = await bcrypt.genSalt(8);
|
||||
hash = await bcrypt.hash(password, salt);
|
||||
hash = await hashPassword(password);
|
||||
}
|
||||
|
||||
// Generate secret
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import bcrypt from 'bcryptjs';
|
||||
import { hashPassword } from '@/misc/password.js';
|
||||
import { secureRndstr } from '@/misc/secure-rndstr.js';
|
||||
import { Users, UserProfiles } from '@/models/index.js';
|
||||
import { ApiError } from '@/server/api/error.js';
|
||||
|
@ -17,8 +17,6 @@ export const meta = {
|
|||
password: {
|
||||
type: 'string',
|
||||
optional: false, nullable: false,
|
||||
minLength: 8,
|
||||
maxLength: 8,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -46,18 +44,15 @@ export default define(meta, paramDef, async (ps) => {
|
|||
throw new ApiError('IS_ADMIN');
|
||||
}
|
||||
|
||||
const passwd = secureRndstr(8, true);
|
||||
|
||||
// Generate hash of password
|
||||
const hash = bcrypt.hashSync(passwd);
|
||||
const password = secureRndstr(8, true);
|
||||
|
||||
await UserProfiles.update({
|
||||
userId: user.id,
|
||||
}, {
|
||||
password: hash,
|
||||
password: await hashPassword(password),
|
||||
});
|
||||
|
||||
return {
|
||||
password: passwd,
|
||||
password,
|
||||
};
|
||||
});
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { promisify } from 'node:util';
|
||||
import bcrypt from 'bcryptjs';
|
||||
import * as cbor from 'cbor';
|
||||
import { MINUTE } from '@/const.js';
|
||||
import { comparePassword } from '@/misc/password.js';
|
||||
import {
|
||||
UserProfiles,
|
||||
UserSecurityKeys,
|
||||
|
@ -41,10 +41,7 @@ export const paramDef = {
|
|||
export default define(meta, paramDef, async (ps, user) => {
|
||||
const profile = await UserProfiles.findOneByOrFail({ userId: user.id });
|
||||
|
||||
// Compare password
|
||||
const same = await bcrypt.compare(ps.password, profile.password!);
|
||||
|
||||
if (!same) {
|
||||
if (!(await comparePassword(ps.password, profile.password!))) {
|
||||
throw new ApiError('ACCESS_DENIED');
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { promisify } from 'node:util';
|
||||
import * as crypto from 'node:crypto';
|
||||
import bcrypt from 'bcryptjs';
|
||||
import { UserProfiles, AttestationChallenges } from '@/models/index.js';
|
||||
import { genId } from '@/misc/gen-id.js';
|
||||
import { comparePassword } from '@/misc/password.js';
|
||||
import { ApiError } from '@/server/api/error.js';
|
||||
import define from '../../../define.js';
|
||||
import { hash } from '../../../2fa.js';
|
||||
|
@ -29,10 +29,7 @@ export const paramDef = {
|
|||
export default define(meta, paramDef, async (ps, user) => {
|
||||
const profile = await UserProfiles.findOneByOrFail({ userId: user.id });
|
||||
|
||||
// Compare password
|
||||
const same = await bcrypt.compare(ps.password, profile.password!);
|
||||
|
||||
if (!same) {
|
||||
if (!(await comparePassword(ps.password, profile.password!))) {
|
||||
throw new ApiError('ACCESS_DENIED');
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import bcrypt from 'bcryptjs';
|
||||
import * as speakeasy from 'speakeasy';
|
||||
import * as QRCode from 'qrcode';
|
||||
import config from '@/config/index.js';
|
||||
import { comparePassword } from '@/misc/password.js';
|
||||
import { UserProfiles } from '@/models/index.js';
|
||||
import { ApiError } from '@/server/api/error.js';
|
||||
import define from '../../../define.js';
|
||||
|
@ -26,10 +26,7 @@ export const paramDef = {
|
|||
export default define(meta, paramDef, async (ps, user) => {
|
||||
const profile = await UserProfiles.findOneByOrFail({ userId: user.id });
|
||||
|
||||
// Compare password
|
||||
const same = await bcrypt.compare(ps.password, profile.password!);
|
||||
|
||||
if (!same) {
|
||||
if (!(await comparePassword(ps.password, profile.password!))) {
|
||||
throw new ApiError('ACCESS_DENIED');
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import bcrypt from 'bcryptjs';
|
||||
import { comparePassword } from '@/misc/password.js';
|
||||
import { UserProfiles, UserSecurityKeys, Users } from '@/models/index.js';
|
||||
import { publishMainStream } from '@/services/stream.js';
|
||||
import { ApiError } from '@/server/api/error.js';
|
||||
|
@ -25,10 +25,7 @@ export const paramDef = {
|
|||
export default define(meta, paramDef, async (ps, user) => {
|
||||
const profile = await UserProfiles.findOneByOrFail({ userId: user.id });
|
||||
|
||||
// Compare password
|
||||
const same = await bcrypt.compare(ps.password, profile.password!);
|
||||
|
||||
if (!same) {
|
||||
if (!(await comparePassword(ps.password, profile.password!))) {
|
||||
throw new ApiError('ACCESS_DENIED');
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import bcrypt from 'bcryptjs';
|
||||
import { comparePassword } from '@/misc/password.js';
|
||||
import { UserProfiles } from '@/models/index.js';
|
||||
import { ApiError } from '@/server/api/error.js';
|
||||
import define from '../../../define.js';
|
||||
|
@ -23,10 +23,7 @@ export const paramDef = {
|
|||
export default define(meta, paramDef, async (ps, user) => {
|
||||
const profile = await UserProfiles.findOneByOrFail({ userId: user.id });
|
||||
|
||||
// Compare password
|
||||
const same = await bcrypt.compare(ps.password, profile.password!);
|
||||
|
||||
if (!same) {
|
||||
if (!(await comparePassword(ps.password, profile.password!))) {
|
||||
throw new ApiError('ACCESS_DENIED');
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import bcrypt from 'bcryptjs';
|
||||
import { comparePassword, hashPassword } from '@/misc/password.js';
|
||||
import { UserProfiles } from '@/models/index.js';
|
||||
import { ApiError } from '@/server/api/error.js';
|
||||
import define from '../../define.js';
|
||||
|
@ -24,18 +24,11 @@ export const paramDef = {
|
|||
export default define(meta, paramDef, async (ps, user) => {
|
||||
const profile = await UserProfiles.findOneByOrFail({ userId: user.id });
|
||||
|
||||
// Compare password
|
||||
const same = await bcrypt.compare(ps.currentPassword, profile.password!);
|
||||
|
||||
if (!same) {
|
||||
if (!(await comparePassword(ps.currentPassword, profile.password!))) {
|
||||
throw new ApiError('ACCESS_DENIED');
|
||||
}
|
||||
|
||||
// Generate hash of password
|
||||
const salt = await bcrypt.genSalt(8);
|
||||
const hash = await bcrypt.hash(ps.newPassword, salt);
|
||||
|
||||
await UserProfiles.update(user.id, {
|
||||
password: hash,
|
||||
password: await hashPassword(ps.newPassword),
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import bcrypt from 'bcryptjs';
|
||||
import { comparePassword } from '@/misc/password.js';
|
||||
import { UserProfiles, Users } from '@/models/index.js';
|
||||
import { deleteAccount } from '@/services/delete-account.js';
|
||||
import { ApiError } from '@/server/api/error.js';
|
||||
|
@ -28,10 +28,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
|||
return;
|
||||
}
|
||||
|
||||
// Compare password
|
||||
const same = await bcrypt.compare(ps.password, profile.password!);
|
||||
|
||||
if (!same) {
|
||||
if (!(await comparePassword(ps.password, profile.password!))) {
|
||||
throw new ApiError('ACCESS_DENIED');
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import bcrypt from 'bcryptjs';
|
||||
import { comparePassword } from '@/misc/password.js';
|
||||
import { publishInternalEvent, publishMainStream, publishUserEvent } from '@/services/stream.js';
|
||||
import { Users, UserProfiles } from '@/models/index.js';
|
||||
import generateUserToken from '../../common/generate-native-user-token.js';
|
||||
|
@ -28,10 +28,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
|||
|
||||
const profile = await UserProfiles.findOneByOrFail({ userId: user.id });
|
||||
|
||||
// Compare password
|
||||
const same = await bcrypt.compare(ps.password, profile.password!);
|
||||
|
||||
if (!same) {
|
||||
if (!(await comparePassword(ps.password, profile.password!))) {
|
||||
throw new ApiError('ACCESS_DENIED');
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import bcrypt from 'bcryptjs';
|
||||
import { publishMainStream } from '@/services/stream.js';
|
||||
import config from '@/config/index.js';
|
||||
import { comparePassword } from '@/misc/password.js';
|
||||
import { secureRndstr } from '@/misc/secure-rndstr.js';
|
||||
import { Users, UserProfiles } from '@/models/index.js';
|
||||
import { sendEmail } from '@/services/send-email.js';
|
||||
|
@ -37,10 +37,9 @@ export const paramDef = {
|
|||
export default define(meta, paramDef, async (ps, user) => {
|
||||
const profile = await UserProfiles.findOneByOrFail({ userId: user.id });
|
||||
|
||||
// Compare password
|
||||
const same = await bcrypt.compare(ps.password, profile.password!);
|
||||
|
||||
if (!same) throw new ApiError('ACCESS_DENIED');
|
||||
if (!(await comparePassword(ps.password, profile.password!))) {
|
||||
throw new ApiError('ACCESS_DENIED');
|
||||
}
|
||||
|
||||
if (ps.email != null) {
|
||||
const available = await validateEmailForAccount(ps.email);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import bcrypt from 'bcryptjs';
|
||||
import { hashPassword } from '@/misc/password.js';
|
||||
import { UserProfiles, PasswordResetRequests } from '@/models/index.js';
|
||||
import { DAY, MINUTE } from '@/const.js';
|
||||
import define from '../define.js';
|
||||
|
@ -43,12 +43,8 @@ export default define(meta, paramDef, async (ps, user) => {
|
|||
throw new ApiError('NO_SUCH_RESET_REQUEST');
|
||||
}
|
||||
|
||||
// Generate hash of password
|
||||
const salt = await bcrypt.genSalt(8);
|
||||
const hash = await bcrypt.hash(ps.password, salt);
|
||||
|
||||
await UserProfiles.update(req.userId, {
|
||||
password: hash,
|
||||
password: await hashPassword(ps.password),
|
||||
});
|
||||
|
||||
await PasswordResetRequests.delete(req.id);
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { randomBytes } from 'node:crypto';
|
||||
import { IsNull } from 'typeorm';
|
||||
import Koa from 'koa';
|
||||
import bcrypt from 'bcryptjs';
|
||||
import * as speakeasy from 'speakeasy';
|
||||
import { SECOND, MINUTE, HOUR } from '@/const.js';
|
||||
import config from '@/config/index.js';
|
||||
|
@ -9,6 +8,7 @@ import { Users, Signins, UserProfiles, UserSecurityKeys, AttestationChallenges }
|
|||
import { ILocalUser } from '@/models/entities/user.js';
|
||||
import { genId } from '@/misc/gen-id.js';
|
||||
import { getIpHash } from '@/misc/get-ip-hash.js';
|
||||
import { comparePassword } from '@/misc/password.js';
|
||||
import signin from '../common/signin.js';
|
||||
import { verifyLogin, hash } from '../2fa.js';
|
||||
import { limiter } from '../limiter.js';
|
||||
|
@ -67,7 +67,7 @@ export default async (ctx: Koa.Context) => {
|
|||
const profile = await UserProfiles.findOneByOrFail({ userId: user.id });
|
||||
|
||||
// Compare password
|
||||
const same = await bcrypt.compare(password, profile.password!);
|
||||
const same = await comparePassword(password, profile.password!);
|
||||
|
||||
async function fail(): void {
|
||||
// Append signin history
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import Koa from 'koa';
|
||||
import bcrypt from 'bcryptjs';
|
||||
import { fetchMeta } from '@/misc/fetch-meta.js';
|
||||
import { verifyHcaptcha, verifyRecaptcha } from '@/misc/captcha.js';
|
||||
import { hashPassword } from '@/misc/password.js';
|
||||
import { Users, RegistrationTickets, UserPendings } from '@/models/index.js';
|
||||
import config from '@/config/index.js';
|
||||
import { sendEmail } from '@/services/send-email.js';
|
||||
|
@ -71,17 +71,13 @@ export default async (ctx: Koa.Context) => {
|
|||
if (instance.emailRequiredForSignup) {
|
||||
const code = secureRndstr(16);
|
||||
|
||||
// Generate hash of password
|
||||
const salt = await bcrypt.genSalt(8);
|
||||
const hash = await bcrypt.hash(password, salt);
|
||||
|
||||
await UserPendings.insert({
|
||||
id: genId(),
|
||||
createdAt: new Date(),
|
||||
code,
|
||||
email: emailAddress,
|
||||
username,
|
||||
password: hash,
|
||||
password: await hashPassword(password),
|
||||
});
|
||||
|
||||
const link = `${config.url}/signup-complete/${code}`;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import bcrypt from 'bcryptjs';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { IsNull } from 'typeorm';
|
||||
import { genRsaKeyPair } from '@/misc/gen-key-pair.js';
|
||||
import { hashPassword } from '@/misc/password.js';
|
||||
import { User } from '@/models/entities/user.js';
|
||||
import { UserProfile } from '@/models/entities/user-profile.js';
|
||||
import { genId } from '@/misc/gen-id.js';
|
||||
|
@ -11,11 +11,7 @@ import { db } from '@/db/postgre.js';
|
|||
import generateNativeUserToken from '@/server/api/common/generate-native-user-token.js';
|
||||
|
||||
export async function createSystemUser(username: string): Promise<User> {
|
||||
const password = uuid();
|
||||
|
||||
// Generate hash of password
|
||||
const salt = await bcrypt.genSalt(8);
|
||||
const hash = await bcrypt.hash(password, salt);
|
||||
const password = await hashPassword(uuid());
|
||||
|
||||
// Generate secret
|
||||
const secret = generateNativeUserToken();
|
||||
|
@ -55,7 +51,7 @@ export async function createSystemUser(username: string): Promise<User> {
|
|||
await transactionalEntityManager.insert(UserProfile, {
|
||||
userId: account.id,
|
||||
autoAcceptFollowed: false,
|
||||
password: hash,
|
||||
password,
|
||||
});
|
||||
|
||||
await transactionalEntityManager.insert(UsedUsername, {
|
||||
|
|
Loading…
Reference in a new issue