Added registrar mirrored

This commit is contained in:
SepehrYahyaee
2026-06-15 15:58:31 +03:30
parent 41f81a2f76
commit a7849f915a
13 changed files with 1089 additions and 61 deletions

View File

@@ -6,6 +6,7 @@ import { StringValue } from "ms";
import { AuthGuard, RolesGuard } from "./guards";
import { AuthController } from "./auth.controller";
import { AuthService } from "./auth.service";
import { HashService } from "./providers";
@Module({
imports: [
@@ -25,6 +26,7 @@ import { AuthService } from "./auth.service";
{ provide: APP_GUARD, useClass: AuthGuard },
{ provide: APP_GUARD, useClass: RolesGuard },
AuthService,
HashService,
],
})
export class AuthModule {}

View File

@@ -0,0 +1,8 @@
import { ScryptOptions } from "node:crypto";
export const KEY_LENGTH = 64;
export const SCRYPT_PARAMS: ScryptOptions = {
N: 19456, // CPU/memory cost
r: 8, // block size
p: 1, // parallelization
};

View File

@@ -0,0 +1,68 @@
import {
BinaryLike,
randomBytes,
scrypt,
ScryptOptions,
timingSafeEqual,
} from "node:crypto";
import { promisify } from "node:util";
import { Injectable } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import { KEY_LENGTH, SCRYPT_PARAMS } from "../constants/hash.constants";
const scryptAsync = promisify<
BinaryLike,
BinaryLike,
number,
ScryptOptions,
Buffer
>(scrypt);
@Injectable()
export class HashService {
private readonly pepper: string;
constructor(private readonly configService: ConfigService) {
this.pepper = this.configService.get<string>("HASH_PEPPER");
}
private applyPepper(input: string): string {
return this.pepper ? `${input}${this.pepper}` : input;
}
private generateSalt(bytes: number = 16): string {
return randomBytes(bytes).toString("hex");
}
private async scrypt(password: string): Promise<[string, string]> {
const salt = this.generateSalt();
const pepperedInput = this.applyPepper(password);
const hashedPassword = await scryptAsync(
pepperedInput,
salt,
KEY_LENGTH,
SCRYPT_PARAMS,
);
return [salt, hashedPassword.toString("hex")];
}
private async verifyScrypt(
password: string,
salt: string,
hashedPassword: string,
): Promise<boolean> {
const pepperedInput = this.applyPepper(password);
const derived = await scryptAsync(
pepperedInput,
salt,
KEY_LENGTH,
SCRYPT_PARAMS,
);
const storedBuffer = Buffer.from(hashedPassword, "hex");
if (derived.length !== storedBuffer.length) return false;
return timingSafeEqual(derived, storedBuffer);
}
}

View File

@@ -0,0 +1 @@
export { HashService } from "./hash.provider";

View File

@@ -1,7 +1,6 @@
export interface JwtPayload {
sub: string;
role: string;
clientId?: string;
iat?: number;
exp?: number;
}