tethys.backend/providers/HashDriver/index.ts

142 lines
4.0 KiB
TypeScript
Raw Normal View History

2023-03-03 15:54:28 +00:00
// const bcrypt = require("bcrypt");
import bcrypt from 'bcryptjs';
2024-03-14 19:25:27 +00:00
// 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;
};
2023-03-03 15:54:28 +00:00
const saltRounds = 10;
2024-03-14 19:25:27 +00:00
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,
2024-03-14 19:25:27 +00:00
};
}
2023-03-03 15:54:28 +00:00
public async make(value: string) {
const hashedValue = bcrypt.hashSync(value, saltRounds);
return hashedValue;
2023-03-03 15:54:28 +00:00
}
public async verify(hashedValue: string, plainValue: string) {
let newHash: string;
if (hashedValue.includes('$2y$10$')) {
newHash = hashedValue.replace('$2y$10$', '$2a$10$');
2023-03-03 15:54:28 +00:00
} else {
newHash = hashedValue;
}
return await bcrypt.compareSync(plainValue, newHash);
}
2024-03-14 19:25:27 +00:00
/**
* 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;
// }
}
2024-03-14 19:25:27 +00:00
/**
* Factory function to reference the driver
* inside the config file.
*/
export function laravelDriver(config: PbkdfConfig): ManagerDriverFactory {
2024-03-14 19:25:27 +00:00
return () => {
return new LaravelDriver(config);
};
}