UNPKG

@lifeworld/lucia

Version:

A simple and flexible authentication library

68 lines (67 loc) 2.59 kB
import { decodeHex, encodeBase32LowerCaseNoPadding, encodeHexLowerCase } from "@oslojs/encoding"; import { generateRandomString } from "@oslojs/crypto/random"; import { constantTimeEqual } from "@oslojs/crypto/subtle"; import { scrypt } from "./scrypt/index.js"; async function generateScryptKey(data, salt, blockSize = 16) { const encodedData = new TextEncoder().encode(data); const encodedSalt = new TextEncoder().encode(salt); const keyUint8Array = await scrypt(encodedData, encodedSalt, { N: 16384, r: blockSize, p: 1, dkLen: 64 }); return new Uint8Array(keyUint8Array); } const random = { read(bytes) { crypto.getRandomValues(bytes); } }; export function generateId(length) { const alphabet = "abcdefghijklmnopqrstuvwxyz0123456789"; return generateRandomString(random, alphabet, length); } export function generateIdFromEntropySize(size) { const buffer = crypto.getRandomValues(new Uint8Array(size)); return encodeBase32LowerCaseNoPadding(buffer); } export class Scrypt { async hash(password) { const salt = encodeHexLowerCase(crypto.getRandomValues(new Uint8Array(16))); const key = await generateScryptKey(password.normalize("NFKC"), salt); return `${salt}:${encodeHexLowerCase(key)}`; } async verify(hash, password) { const parts = hash.split(":"); if (parts.length !== 2) return false; const [salt, key] = parts; const targetKey = await generateScryptKey(password.normalize("NFKC"), salt); return constantTimeEqual(targetKey, decodeHex(key)); } } export class LegacyScrypt { async hash(password) { const salt = encodeHexLowerCase(crypto.getRandomValues(new Uint8Array(16))); const key = await generateScryptKey(password.normalize("NFKC"), salt); return `s2:${salt}:${encodeHexLowerCase(key)}`; } async verify(hash, password) { const parts = hash.split(":"); if (parts.length === 2) { const [salt, key] = parts; const targetKey = await generateScryptKey(password.normalize("NFKC"), salt, 8); const result = constantTimeEqual(targetKey, decodeHex(key)); return result; } if (parts.length !== 3) return false; const [version, salt, key] = parts; if (version === "s2") { const targetKey = await generateScryptKey(password.normalize("NFKC"), salt); return constantTimeEqual(targetKey, decodeHex(key)); } return false; } }