From 9075ea8c9148e8517d62ff82036683d7b492198b Mon Sep 17 00:00:00 2001 From: Matthew Raymer Date: Fri, 15 Sep 2023 09:44:10 -0400 Subject: [PATCH] Debugged encrypt before sending notification --- Dockerfile | 2 + src/main.ts | 18 ++++----- src/notificationService.ts | 76 +++++++++++++++++++++++++------------- src/vapidService.ts | 1 + 4 files changed, 62 insertions(+), 35 deletions(-) diff --git a/Dockerfile b/Dockerfile index 4412a9d..93027fc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,6 +3,8 @@ FROM node:18.17.1-alpine3.17 RUN mkdir -p /usr/src/app/data WORKDIR /usr/src/app +RUN apk add bash + COPY package*.json ./ COPY tsconfig.json ./ COPY .eslintrc.json ./ diff --git a/src/main.ts b/src/main.ts index 9b272ef..6c96084 100644 --- a/src/main.ts +++ b/src/main.ts @@ -14,7 +14,7 @@ import { dirname, join } from 'path'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); -export interface Subscription { +export interface BrowserSubscription { endpoint: string; keys: { p256dh: string; @@ -27,15 +27,13 @@ class Server { private port: number; private worker?: Worker; private subscriptionService: SubscriptionService = SubscriptionService.getInstance(); - private notificationService: NotificationService; + private notificationService: NotificationService = NotificationService.getInstance(); dbService: DBService = DBService.getInstance(); vapidService: VapidService = VapidService.getInstance(); - private message: Message; constructor(port: number) { this.app = express(); this.port = port; - this.notificationService = new NotificationService(); this.setupRoutes(); this.startWorker(); @@ -46,21 +44,23 @@ class Server { private setupRoutes(): void { this.app.use(express.json()) this.app.post('/web-push/subscribe', async (req: Request, res: Response) => { - const subscription = req.body as Subscription; + const subscription = req.body as BrowserSubscription; + const message = { "title": "You are subscribed." } as Message; console.log("/web-push/subscribe", req.body); await this.subscriptionService.addSubscription(subscription); + await this.notificationService.sendNotification(subscription, message); res.status(201).send(); }); this.app.post('/web-push/unsubscribe', async (req: Request, res: Response) => { - const subscription = req.body as Subscription; + const subscription = req.body as BrowserSubscription; console.log(subscription); res.status(501).send(); }); this.app.post('/web-push/mute', async (req: Request, res: Response) => { - const subscription = req.body as Subscription; + const subscription = req.body as BrowserSubscription; console.log(subscription); res.status(501).send(); @@ -88,8 +88,7 @@ class Server { this.worker.on('message', (message) => { console.log(message); - this.message = { "title": "Check TimeSafari"} as Message; - this.notificationService.broadcast(this.message); + }); this.worker.on('error', (error) => { @@ -137,6 +136,7 @@ const executeAsyncFunction = async () => { } }; + setTimeout(() => { executeAsyncFunction().catch(error => { // Handle any errors here diff --git a/src/notificationService.ts b/src/notificationService.ts index 310c0e3..6d197ec 100644 --- a/src/notificationService.ts +++ b/src/notificationService.ts @@ -1,10 +1,10 @@ -import SubscriptionService from './subscriptionService.js'; +//import SubscriptionService from './subscriptionService.js'; import VapidService from './vapidService.js'; import { VapidKeys } from './VapidKeys.js'; import * as https from 'https'; import * as http_ece from 'http_ece'; import crypto from 'crypto'; -import { Subscription } from "./Subscription.js" +//import { Subscription } from "./Subscription.js" export interface Message { title: string; @@ -12,34 +12,44 @@ export interface Message { [key: string]: any; } -export class NotificationService { - private subscriptionService: SubscriptionService = SubscriptionService.getInstance(); +export interface BrowserSubscription { + endpoint: string; + keys: { + p256dh: string; + auth: string; + }; +} + + +export class NotificationService { + private static instance: NotificationService; +// private subscriptionService: SubscriptionService = SubscriptionService.getInstance(); private vapidService: VapidService = VapidService.getInstance(); constructor() { } + + public static getInstance(): NotificationService { + if (!NotificationService.instance) { + NotificationService.instance = new NotificationService(); + } + return NotificationService.instance; + } private generateSalt(length = 16): Buffer { return crypto.randomBytes(length); } - async broadcast(message: Message): Promise { - const subscriptions = await this.subscriptionService.fetchSubscriptions(); - - for (const subscription of subscriptions) { - await this.pushToEndpoint(subscription, message); - } - } - - async sendNotification(subscription: Subscription, message: Message) { + async sendNotification(subscription: BrowserSubscription, message: Message) { await this.pushToEndpoint(subscription, message); } - private async pushToEndpoint(subscription: Subscription, message: Message): Promise { - const payload = JSON.stringify(message); + private async pushToEndpoint(subscription: BrowserSubscription, message: Message): Promise { + const payloadString = JSON.stringify(message); + const payloadBuffer = Buffer.from(payloadString, 'utf-8'); - const encrypted = this.encrypt(subscription.keys_p256dh, subscription.keys_auth, payload); + const encrypted = await this.encrypt(subscription.keys.p256dh, subscription.keys.auth, payloadBuffer); const endpoint = subscription.endpoint; const vapidHeaders = await this.vapidService.createVapidAuthHeader(endpoint, 12 * 60 * 60, 'mailto:example@example.com'); @@ -77,18 +87,32 @@ export class NotificationService { }); } - private encrypt(publicKey: string, auth: string, payload: string): Buffer { - http_ece.keys = { - 'p256dh': publicKey, - 'auth': auth - }; - const vapidKeys: VapidKeys = this.vapidService.getVapidKeys()[0]; + private async encrypt(publicKey: string, auth: string, payload: Buffer): Promise { + try { + console.log('Public Key:', publicKey); + console.log('Auth:', auth); + + const vapidKeys: VapidKeys[] = await this.vapidService.getVapidKeys(); + const vapidkey: VapidKeys = vapidKeys[0]; + const vapidPrivateKeyBase64: string = vapidkey['privateKey']; + console.log(vapidPrivateKeyBase64); + const vapidPrivateKeyBuffer: Buffer = Buffer.from(vapidPrivateKeyBase64, 'base64'); + const ecdh = crypto.createECDH('prime256v1'); + ecdh.setPrivateKey(vapidPrivateKeyBuffer); + const publicKeyBuffer: Buffer = Buffer.from(publicKey, 'base64'); +// const authBuffer: Buffer = Buffer.from(auth, 'base64'); + return http_ece.encrypt(payload, { 'salt': this.generateSalt(), - 'dh': vapidKeys.publicKey, - 'keyid': 'p256dh', - 'contentEncoding': 'aes128gcm' + 'privateKey': ecdh, // Your VAPID private key + 'publicKey': publicKeyBuffer, // Client's public key from the subscription object subscription.keys.p256dh + 'authSecret': auth // Client's auth secret from the subscription object subscription.keys.auth }); - } + } catch (error) { + console.error('Error encrypting payload:', error); + // Handle the error as appropriate for your application + throw error; + } + } } diff --git a/src/vapidService.ts b/src/vapidService.ts index 6545c7c..a52ae83 100644 --- a/src/vapidService.ts +++ b/src/vapidService.ts @@ -66,6 +66,7 @@ class VapidService { */ async getVapidKeys(): Promise { + console.log("getVapidKeys"); let result = await this.dbService.getVapidKeys(); return result; }