#!/usr/bin/env node /** * Helper CLI to generate SESSION_SECRET and STATE_SIGNING_SECRET values. * * Usage: * node scripts/generate-session-secrets.js * * Optional flags: * --session-bytes= (default: 48) * --state-bytes= (default: 32) * --derive-state (derive STATE_SIGNING_SECRET from SESSION_SECRET) */ const { randomBytes } = require('node:crypto'); const defaults = { sessionBytes: 48, stateBytes: 32, deriveState: false, }; const options = process.argv.slice(2).reduce((acc, arg) => { if (arg.startsWith('--session-bytes=')) { acc.sessionBytes = parseInt(arg.split('=')[1], 10); } else if (arg.startsWith('--state-bytes=')) { acc.stateBytes = parseInt(arg.split('=')[1], 10); } else if (arg === '--derive-state') { acc.deriveState = true; } return acc; }, { ...defaults }); const validateBytes = (value, flagName) => { if (!Number.isInteger(value) || value <= 0) { console.error(`Invalid value for ${flagName}. Expected a positive integer, received: ${value}`); process.exit(1); } }; validateBytes(options.sessionBytes, '--session-bytes'); if (!options.deriveState) { validateBytes(options.stateBytes, '--state-bytes'); } const generateSecret = (bytes) => randomBytes(bytes).toString('base64'); const sessionSecret = generateSecret(options.sessionBytes); const stateSecret = options.deriveState ? `${sessionSecret}-state-signing` : generateSecret(options.stateBytes); console.log(`SESSION_SECRET=${sessionSecret}`); console.log(`STATE_SIGNING_SECRET=${stateSecret}`); console.log('\n# Copy the values above into your deployment secret store (.env, Vault, etc.).'); console.log('# Keep them private and rotate on a regular schedule.');