UNPKG

passport-cookie-session

Version:

Encrypted, cookie-based session middleware compatible with PassportJS

147 lines (123 loc) 6.29 kB
const withTimeout = require('./timeout'); async function checkEncryptionFunctions(encrypt, decrypt, key, timeout) { const testData = { foo: "bar", date: Date.now() }; const testDataString = JSON.stringify(testData); try { // Attempt to encrypt within the timeout const encrypted = await withTimeout("Encryption", encrypt, [testDataString, key], timeout); // Attempt to decrypt within the timeout const decrypted = await withTimeout("Decryption", decrypt, [encrypted, key], timeout); try { const parsed = JSON.parse(decrypted); // Check if decrypted data matches the original test data if (parsed.foo !== testData.foo || typeof parsed.date !== 'number') { console.error('[Data Mismatch] Decrypted data does not match the original test data.'); console.error('→ Verify that your encrypt and decrypt functions correctly preserve data integrity.'); throw new Error('Decrypted data integrity check failed.'); } else { console.log('[Success] Encryption and decryption test passed. Custom functions work correctly.'); } } catch (parseError) { console.error(`[JSON Parse Error] Unable to parse decrypted data: ${parseError.message}`); console.error('→ Decrypted output might be corrupted or invalid JSON. Check your decrypt function.'); throw new Error('Failed to parse decrypted data.'); } } catch (error) { console.error(`[Encryption/Decryption Check Error] ${error.message}`); console.error('→ Please check your custom encrypt/decrypt function implementations and ensure they call the callback properly.'); throw error; // re-throw to allow calling code to handle } } function validateOptions(options) { const { name, keys, cookie: userCookieOptions = {}, maxCookieSize, encrypt, decrypt, timeout, checkEncryption, } = options; if (!Array.isArray(keys) || keys.length === 0 || !keys.every(k => typeof k === 'string' && k.length > 0)) { throw new TypeError('`keys` must be a non-empty array of non-empty strings.'); } if (name !== undefined && (typeof name !== 'string' || !name.trim())) { throw new TypeError('`name` must be a non-empty string.'); } if (maxCookieSize !== undefined) { if (typeof maxCookieSize !== 'number' || !Number.isFinite(maxCookieSize) || maxCookieSize <= 0) { throw new TypeError('`maxCookieSize` must be a finite positive number (in bytes).'); } } if (encrypt !== undefined) { if (typeof encrypt !== "function") { throw new TypeError('`encrypt` must be a function.'); } if (encrypt.constructor.name !== 'AsyncFunction') { throw new TypeError('`encrypt` must be an async function (declared with `async`).'); } if (encrypt.length < 2) { throw new TypeError('`encrypt` must accept at least 3 arguments: (data, signingKey).'); } } if (decrypt !== undefined) { if (typeof decrypt !== "function") { throw new TypeError('`decrypt` must be a function.'); } if (decrypt.constructor.name !== 'AsyncFunction') { throw new TypeError('`decrypt` must be an async function (declared with `async`).'); } if (decrypt.length < 2) { throw new TypeError('`decrypt` must accept at least 3 arguments: (data, signingKey).'); } } if ((encrypt && !decrypt) || (!encrypt && decrypt)) { throw new TypeError('Both `encrypt` and `decrypt` functions must be provided together.'); } if (typeof userCookieOptions !== 'object' || userCookieOptions === null) { throw new TypeError('`cookie` option must be an object.'); } const allowedCookieOptions = ['httpOnly', 'secure', 'sameSite', 'path', 'domain', 'maxAge']; for (const key of Object.keys(userCookieOptions)) { if (!allowedCookieOptions.includes(key)) { console.warn(`Warning: Unknown cookie option "${key}" detected.`); } } if ('sameSite' in userCookieOptions) { if (typeof userCookieOptions.sameSite !== 'string') { throw new TypeError('`cookie.sameSite` must be a string.'); } const allowedSameSiteValues = ['lax', 'strict', 'none']; if (!allowedSameSiteValues.includes(userCookieOptions.sameSite.toLowerCase())) { throw new TypeError('`cookie.sameSite` must be one of: "lax", "strict", or "none".'); } } if ('secure' in userCookieOptions && typeof userCookieOptions.secure !== 'boolean') { throw new TypeError('`cookie.secure` must be a boolean.'); } if ('httpOnly' in userCookieOptions && typeof userCookieOptions.httpOnly !== 'boolean') { throw new TypeError('`cookie.httpOnly` must be a boolean.'); } if ('path' in userCookieOptions && typeof userCookieOptions.path !== 'string') { throw new TypeError('`cookie.path` must be a string.'); } if ('domain' in userCookieOptions && typeof userCookieOptions.domain !== 'string') { throw new TypeError('`cookie.domain` must be a string.'); } if ('maxAge' in userCookieOptions) { if (typeof userCookieOptions.maxAge !== 'number' || !Number.isFinite(userCookieOptions.maxAge) || userCookieOptions.maxAge < 0) { throw new TypeError('`cookie.maxAge` must be a finite non-negative number (in seconds).'); } } if (timeout !== undefined) { if (typeof timeout !== 'number' || !Number.isFinite(timeout) || timeout <= 0) { throw new TypeError('`timeout` must be a finite positive number (in milliseconds).'); } } if (checkEncryption !== undefined && typeof checkEncryption !== 'boolean') { throw new TypeError('`checkEncryption` must be a boolean.'); } return Boolean(encrypt && decrypt); } module.exports = { validateOptions, checkEncryptionFunctions };