48 lines
1.1 KiB
JavaScript
48 lines
1.1 KiB
JavaScript
import crypto from 'node:crypto'
|
|
|
|
const SCRYPT_KEY_LENGTH = 64
|
|
|
|
function scryptAsync(password, salt) {
|
|
return new Promise((resolve, reject) => {
|
|
crypto.scrypt(password, salt, SCRYPT_KEY_LENGTH, (error, derivedKey) => {
|
|
if (error) {
|
|
reject(error)
|
|
return
|
|
}
|
|
|
|
resolve(derivedKey)
|
|
})
|
|
})
|
|
}
|
|
|
|
export async function hashPassword(password) {
|
|
const salt = crypto.randomBytes(16).toString('hex')
|
|
const derivedKey = await scryptAsync(password, salt)
|
|
return `${salt}:${derivedKey.toString('hex')}`
|
|
}
|
|
|
|
export async function verifyPassword(password, storedHash) {
|
|
const [salt, originalHash] = storedHash.split(':')
|
|
|
|
if (!salt || !originalHash) {
|
|
return false
|
|
}
|
|
|
|
const derivedKey = await scryptAsync(password, salt)
|
|
const originalBuffer = Buffer.from(originalHash, 'hex')
|
|
|
|
if (originalBuffer.length !== derivedKey.length) {
|
|
return false
|
|
}
|
|
|
|
return crypto.timingSafeEqual(originalBuffer, derivedKey)
|
|
}
|
|
|
|
export function createSessionToken() {
|
|
return crypto.randomBytes(32).toString('hex')
|
|
}
|
|
|
|
export function hashSessionToken(token) {
|
|
return crypto.createHash('sha256').update(token).digest('hex')
|
|
}
|