demos for spacedust

keep all the keys together

i forget why this seemed important

+23 -22
+23 -22
server/index.js
··· 2 2 "use strict"; 3 3 4 4 import fs from 'node:fs'; 5 + import { randomBytes } from 'node:crypto'; 5 6 import http from 'http'; 6 7 import * as jose from 'jose'; 7 8 import cookie from 'cookie'; ··· 132 133 }; 133 134 } 134 135 135 - const getOrCreateKeys = filename => { 136 - let keys; 136 + const getOrCreateSecrets = filename => { 137 + let secrets; 137 138 try { 138 - const data = fs.readFileSync(filename); 139 - keys = JSON.parse(data); 139 + const serialized = fs.readFileSync(filename); 140 + secrets = JSON.parse(serialized); 140 141 } catch (err) { 141 142 if (err.code != 'ENOENT') throw err; 142 - keys = webpush.generateVAPIDKeys(); 143 - const data = JSON.stringify(keys); 144 - fs.writeFileSync(filename, data); 143 + secrets = { 144 + pushKeys: webpush.generateVAPIDKeys(), 145 + appSecret: randomBytes(32).toString('hex'), 146 + }; 147 + const serialized = JSON.stringify(secrets); 148 + fs.writeFileSync(filename, serialized); 145 149 } 146 - console.log(`Keys ready with pubkey: ${keys.publicKey}`); 147 - return keys; 150 + console.log(`Keys ready with webpush pubkey: ${secrets.pushKeys.publicKey}`); 151 + return secrets; 148 152 } 149 153 150 154 const getRequesBody = async req => new Promise((resolve, reject) => { ··· 236 240 res.end('{"oh": "hi"}'); 237 241 }; 238 242 239 - const requestListener = (pubkey, jwks, appSecret, db) => (req, res) => { 243 + const requestListener = (secrets, jwks, db) => (req, res) => { 240 244 if (req.method === 'GET' && req.url === '/') { 241 - return handleIndex(req, res, { PUBKEY: pubkey }); 245 + return handleIndex(req, res, { PUBKEY: secrets.pushKeys.publicKey }); 242 246 } 243 247 if (req.method === 'GET' && req.url === '/service-worker.js') { 244 - return handleServiceWorker(req, res, { PUBKEY: pubkey }); 248 + return handleServiceWorker(req, res, { PUBKEY: secrets.pushKeys.publicKey }); 245 249 } 246 250 247 251 if (req.method === 'OPTIONS' && req.url === '/verify') { ··· 250 254 } 251 255 if (req.method === 'POST' && req.url === '/verify') { 252 256 res.setHeaders(new Headers(CORS_PERMISSIVE(req))); 253 - return handleVerify(db, req, res, jwks, appSecret); 257 + return handleVerify(db, req, res, jwks, secrets.appSecret); 254 258 } 255 259 256 260 if (req.method === 'OPTIONS' && req.url === '/subscribe') { ··· 259 263 } 260 264 if (req.method === 'POST' && req.url === '/subscribe') { 261 265 res.setHeaders(new Headers(CORS_PERMISSIVE(req))); 262 - return handleSubscribe(db, req, res, appSecret); 266 + return handleSubscribe(db, req, res, secrets.appSecret); 263 267 } 264 268 265 269 res.writeHead(200); ··· 267 271 } 268 272 269 273 const main = env => { 270 - if (!env.KEY_FILE) throw new Error('KEY_FILE is required to run'); 271 - const keys = getOrCreateKeys(env.KEY_FILE); 274 + if (!env.SECRETS_FILE) throw new Error('SECRETS_FILE is required to run'); 275 + const secrets = getOrCreateSecrets(env.SECRETS_FILE); 272 276 webpush.setVapidDetails( 273 277 'mailto:phil@bad-example.com', 274 - keys.publicKey, 275 - keys.privateKey, 278 + secrets.pushKeys.publicKey, 279 + secrets.pushKeys.privateKey, 276 280 ); 277 - 278 - if (!env.APP_SECRET) throw new Error('APP_SECRET is required to run'); 279 - const appSecret = env.APP_SECRET; 280 281 281 282 const whoamiHost = env.WHOAMI_HOST ?? 'https://who-am-i.microcosm.blue'; 282 283 const jwks = jose.createRemoteJWKSet(new URL(`${whoamiHost}/.well-known/jwks.json`)); ··· 293 294 const port = parseInt(env.PORT ?? 8000, 10); 294 295 295 296 http 296 - .createServer(requestListener(keys.publicKey, jwks, appSecret, db)) 297 + .createServer(requestListener(secrets, jwks, db)) 297 298 .listen(port, host, () => console.log(`listening at http://${host}:${port}`)); 298 299 }; 299 300