UNPKG

uncsrf

Version:

Single API for CSRF functions, working in Node.js, Browsers and other runtimes

52 lines (50 loc) 1.78 kB
const webCrypto = globalThis.crypto; const subtle = webCrypto.subtle; const getRandomValues = (array) => webCrypto.getRandomValues(array); const defaultEncryptAlgorithm = "AES-CBC"; const importEncryptSecret = async (secret, encryptAlgorithm) => { const keyData = new TextEncoder().encode(secret ?? randomEncryptSecret()); return await subtle.importKey( "raw", keyData, { name: encryptAlgorithm || defaultEncryptAlgorithm }, false, ["encrypt", "decrypt"] ); }; const create = async (secret, encryptSecret, encryptAlgorithm) => { const iv = getRandomValues(new Uint8Array(16)); const encrypted = await subtle.encrypt( { name: encryptAlgorithm || defaultEncryptAlgorithm, iv }, encryptSecret, new TextEncoder().encode(secret) ); const encryptedBuffer = Buffer.from(new Uint8Array(encrypted)); return `${Buffer.from(iv).toString("base64")}:${encryptedBuffer.toString( "base64" )}`; }; const verify = async (secret, token, encryptSecret, encryptAlgorithm) => { const [iv, encrypted] = token.split(":"); if (!iv || !encrypted) { return false; } let decrypted; try { const encodedDecrypted = await subtle.decrypt( { name: encryptAlgorithm || defaultEncryptAlgorithm, iv: Buffer.from(iv, "base64") }, encryptSecret, Buffer.from(encrypted, "base64") ); decrypted = new TextDecoder().decode(encodedDecrypted); } catch { return false; } return decrypted === secret; }; const randomSecret = () => webCrypto.randomUUID(); const randomEncryptSecret = () => [...crypto.getRandomValues(new Uint8Array(16))].map((b) => b.toString(16).padStart(2, "0")).join(""); export { create, defaultEncryptAlgorithm, importEncryptSecret, randomEncryptSecret, randomSecret, verify };