// const bcrypt = require("bcrypt"); import bcrypt from 'bcryptjs'; // import bcrypt from 'bcrypt/bcrypt.js' import { HashDriverContract, ManagerDriverFactory } from '@adonisjs/core/types/hash'; import { EnumValidator, RangeValidator, PhcFormatter } from './helpers.js'; /** * Config accepted by the hash driver */ export type PbkdfConfig = { rounds: number; saltSize?: number; version?: number; }; const saltRounds = 10; export class LaravelDriver implements HashDriverContract { private config: PbkdfConfig; // private binding; private phcFormatter = new PhcFormatter(); constructor(config: {}) { this.config = { rounds: 10, saltSize: 16, version: 98, ...config, }; } public async make(value: string) { const hashedValue = bcrypt.hashSync(value, saltRounds); return hashedValue; } public async verify(hashedValue: string, plainValue: string) { let newHash: string; if (hashedValue.includes('$2y$10$')) { newHash = hashedValue.replace('$2y$10$', '$2a$10$'); } else { newHash = hashedValue; } return await bcrypt.compareSync(plainValue, newHash); } /** * Check if the value is a valid hash. This method just checks * for the formatting of the hash. * * ```ts * bcrypt.isValidHash('hello world') // false * bcrypt.isValidHash('$bcrypt$v=98$r=10$Jtxi46WJ26OQ0khsYLLlnw$knXGfuRFsSjXdj88JydPOnUIglvm1S8') * ``` */ public isValidHash(value: string) { try { this.validatePhcString(value); return true; } catch { return false; } } public needsReHash(value: string): boolean { if (value.startsWith('$2b') || value.startsWith('$2a')) { return true; } const phcNode = this.phcFormatter.deserialize(value); if (phcNode.id !== 'bcrypt') { return true; } if (phcNode.version !== this.config.version) { return true; } if (!phcNode.params) { return true; } if (phcNode.params.r !== this.config.rounds) { return true; } return false; } /** * Validate phc hash string */ private validatePhcString(phcString: string) { const phcNode = this.phcFormatter.deserialize(phcString); if (!phcNode.version) { phcNode.version = 97; } if (phcNode.id !== 'bcrypt') { throw new TypeError(`Invalid "id" found in the phc string`); } if (!phcNode.params) { throw new TypeError(`No "params" found in the phc string`); } if (!phcNode.salt) { throw new TypeError(`No "salt" found in the phc string`); } if (!phcNode.hash) { throw new TypeError(`No "hash" found in the phc string`); } if (!phcNode.hash.byteLength) { throw new TypeError(`No "hash" found in the phc string`); } RangeValidator.validate('salt.byteLength', phcNode.salt.byteLength, [8, 1024]); EnumValidator.validate('version', phcNode.version, [97, 98]); RangeValidator.validate('r', phcNode.params.r, [4, 31]); return { id: phcNode.id, version: phcNode.version, hash: phcNode.hash, salt: phcNode.salt, params: { r: phcNode.params.r, }, }; } /** * Dynamically importing underlying binding */ // private async importBinding() { // if (this.binding) { // return this.binding; // } // this.binding = await import('bcrypt'); // return this.binding; // } } /** * Factory function to reference the driver * inside the config file. */ export function laravelDriver(config: PbkdfConfig): ManagerDriverFactory { return () => { return new LaravelDriver(config); }; }