From 8a9ffc591899b2442f9880305d09c4b859df1b01 Mon Sep 17 00:00:00 2001 From: padreug Date: Mon, 7 Jul 2025 00:38:32 +0200 Subject: [PATCH] feat: Implement secure VAPID key generation for push notifications - Replace random key generation with the web-push library for generating cryptographically secure VAPID keys. - Update console output to guide users on adding keys to their environment configuration. - Enhance error handling for VAPID key generation issues. - Add web-push dependency to package.json and package-lock.json for proper functionality. --- generate-vapid-keys.js | 52 ++++------ package-lock.json | 127 +++++++++++++++++++++++- package.json | 3 +- src/components/nostr/IdentityDialog.vue | 2 +- src/lib/notifications/push.ts | 21 +++- 5 files changed, 169 insertions(+), 36 deletions(-) diff --git a/generate-vapid-keys.js b/generate-vapid-keys.js index 79a8569..40fad25 100644 --- a/generate-vapid-keys.js +++ b/generate-vapid-keys.js @@ -1,36 +1,28 @@ #!/usr/bin/env node -// Simple VAPID key generator for testing push notifications -// In production, you'd want to use proper cryptographic libraries +// Proper VAPID key generator using web-push library +import webpush from 'web-push' console.log('🔑 VAPID Key Generator for Push Notifications') console.log('') -// Generate a random string for testing -function generateTestKey(length = 64) { - const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_' - let result = '' - for (let i = 0; i < length; i++) { - result += chars.charAt(Math.floor(Math.random() * chars.length)) - } - return result -} - -// Generate test VAPID keys -const publicKey = generateTestKey(87) // Base64 URL-safe, typical length -const privateKey = generateTestKey(43) // Base64 URL-safe, typical length - -console.log('📋 Add these to your .env file:') -console.log('') -console.log(`VITE_VAPID_PUBLIC_KEY=${publicKey}`) -console.log(`VITE_PUSH_NOTIFICATIONS_ENABLED=true`) -console.log('') -console.log('âš ī¸ IMPORTANT: These are test keys for development only!') -console.log(' For production, generate proper VAPID keys using:') -console.log(' - web-push library: npx web-push generate-vapid-keys') -console.log(' - online tool: https://vapidkeys.com/') -console.log('') -console.log('🔐 Private key (keep secure, for backend only):') -console.log(`VAPID_PRIVATE_KEY=${privateKey}`) -console.log('') -console.log('✅ Once added, restart your dev server to apply the changes.') \ No newline at end of file +try { + // Generate proper VAPID keys using web-push + const vapidKeys = webpush.generateVAPIDKeys() + + console.log('📋 Add these to your .env file:') + console.log('') + console.log(`VITE_VAPID_PUBLIC_KEY=${vapidKeys.publicKey}`) + console.log(`VITE_PUSH_NOTIFICATIONS_ENABLED=true`) + console.log('') + console.log('🔐 Private key (keep secure, for backend only):') + console.log(`VAPID_PRIVATE_KEY=${vapidKeys.privateKey}`) + console.log('') + console.log('✅ Once added, restart your dev server to apply the changes.') + console.log('') + console.log('â„šī¸ These are cryptographically secure VAPID keys suitable for production.') + +} catch (error) { + console.error('❌ Error generating VAPID keys:', error) + process.exit(1) +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 757f82d..668913a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -60,7 +60,8 @@ "vite-plugin-image-optimizer": "^1.1.7", "vite-plugin-inspect": "^0.8.3", "vite-plugin-pwa": "^0.21.1", - "vue-tsc": "^2.2.0" + "vue-tsc": "^2.2.0", + "web-push": "^3.6.7" } }, "node_modules/@ampproject/remapping": { @@ -5544,6 +5545,16 @@ "node": ">=0.4.0" } }, + "node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, "node_modules/agentkeepalive": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", @@ -5709,6 +5720,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + } + }, "node_modules/async": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", @@ -5851,6 +5875,13 @@ "dev": true, "license": "MIT" }, + "node_modules/bn.js": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", + "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "dev": true, + "license": "MIT" + }, "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", @@ -5958,6 +5989,13 @@ "node": "*" } }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -7018,6 +7056,16 @@ "dev": true, "license": "MIT" }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/ejs": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", @@ -8669,6 +8717,16 @@ "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", "license": "MIT" }, + "node_modules/http_ece": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http_ece/-/http_ece-1.2.0.tgz", + "integrity": "sha512-JrF8SSLVmcvc5NducxgyOrKXe3EsyHMgBFgSaIUGmArKe+rwr0uphRkRXvwiom3I+fpIfoItveHrfudL8/rxuA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + } + }, "node_modules/http-cache-semantics": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", @@ -8690,6 +8748,20 @@ "node": ">=10.19.0" } }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/humanize-ms": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", @@ -9534,6 +9606,29 @@ "node": ">=8" } }, + "node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -10450,6 +10545,13 @@ "mini-svg-data-uri": "cli.js" } }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true, + "license": "ISC" + }, "node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", @@ -12304,8 +12406,7 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true, - "license": "MIT", - "optional": true + "license": "MIT" }, "node_modules/semver": { "version": "6.3.1", @@ -14029,6 +14130,26 @@ "defaults": "^1.0.3" } }, + "node_modules/web-push": { + "version": "3.6.7", + "resolved": "https://registry.npmjs.org/web-push/-/web-push-3.6.7.tgz", + "integrity": "sha512-OpiIUe8cuGjrj3mMBFWY+e4MMIkW3SVT+7vEIjvD9kejGUypv8GPDf84JdPWskK8zMRIJ6xYGm+Kxr8YkPyA0A==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "asn1.js": "^5.3.0", + "http_ece": "1.2.0", + "https-proxy-agent": "^7.0.0", + "jws": "^4.0.0", + "minimist": "^1.2.5" + }, + "bin": { + "web-push": "src/cli.js" + }, + "engines": { + "node": ">= 16" + } + }, "node_modules/web-vitals": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-3.5.2.tgz", diff --git a/package.json b/package.json index c974dfd..2a41448 100644 --- a/package.json +++ b/package.json @@ -69,7 +69,8 @@ "vite-plugin-image-optimizer": "^1.1.7", "vite-plugin-inspect": "^0.8.3", "vite-plugin-pwa": "^0.21.1", - "vue-tsc": "^2.2.0" + "vue-tsc": "^2.2.0", + "web-push": "^3.6.7" }, "build": { "appId": "com.yourdomain.aio-shadcn-vite", diff --git a/src/components/nostr/IdentityDialog.vue b/src/components/nostr/IdentityDialog.vue index 93e3d01..82cd4a7 100644 --- a/src/components/nostr/IdentityDialog.vue +++ b/src/components/nostr/IdentityDialog.vue @@ -1,5 +1,5 @@