Updates and a remaining authentication error to the push service at Mozilla
This commit is contained in:
19170
package-lock.json
generated
19170
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
81
package.json
81
package.json
@@ -4,55 +4,56 @@
|
|||||||
"description": "Minimalistic boilerplate to quick-start Node.js development in TypeScript.",
|
"description": "Minimalistic boilerplate to quick-start Node.js development in TypeScript.",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 18.12 <19"
|
"node": ">= 18.12 <19"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/body-parser": "^1.19.2",
|
"@types/body-parser": "^1.19.2",
|
||||||
"@types/express": "^4.17.17",
|
"@types/express": "^4.17.17",
|
||||||
"@types/jest": "~29.5",
|
"@types/jest": "~29.5",
|
||||||
"@types/jsonwebtoken": "^9.0.2",
|
"@types/jsonwebtoken": "^9.0.2",
|
||||||
"@types/node": "~20",
|
"@types/node": "~20",
|
||||||
"@types/sqlite3": "^3.1.8",
|
"@types/sqlite3": "^3.1.8",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.4.0",
|
"@typescript-eslint/eslint-plugin": "^6.4.0",
|
||||||
"@typescript-eslint/parser": "^6.4.0",
|
"@typescript-eslint/parser": "^6.4.0",
|
||||||
"eslint": "~8.47",
|
"eslint": "~8.47",
|
||||||
"eslint-config-prettier": "~9.0",
|
"eslint-config-prettier": "~9.0",
|
||||||
"eslint-plugin-jest": "~27.2",
|
"eslint-plugin-jest": "~27.2",
|
||||||
"jest": "~29.6",
|
"jest": "~29.6",
|
||||||
"prettier": "~3.0",
|
"prettier": "~3.0",
|
||||||
"rimraf": "~5.0",
|
"rimraf": "~5.0",
|
||||||
"ts-api-utils": "~1.0",
|
"ts-api-utils": "~1.0",
|
||||||
"ts-jest": "~29.1",
|
"ts-jest": "~29.1",
|
||||||
"typescript": "~5.1"
|
"typescript": "~5.1"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node build/src/main.js",
|
"start": "node build/src/main.js",
|
||||||
"clean": "rimraf coverage build tmp",
|
"clean": "rimraf coverage build tmp",
|
||||||
"prebuild": "npm run lint",
|
"prebuild": "npm run lint",
|
||||||
"build": "tsc -p tsconfig.json",
|
"build": "tsc -p tsconfig.json",
|
||||||
"build:watch": "tsc -w -p tsconfig.json",
|
"build:watch": "tsc -w -p tsconfig.json",
|
||||||
"build:release": "npm run clean && tsc -p tsconfig.release.json",
|
"build:release": "npm run clean && tsc -p tsconfig.release.json",
|
||||||
"lint": "eslint . --ext .ts --ext .mts",
|
"lint": "eslint . --ext .ts --ext .mts",
|
||||||
"test": "jest --coverage",
|
"test": "jest --coverage",
|
||||||
"prettier": "prettier --config .prettierrc --write .",
|
"prettier": "prettier --config .prettierrc --write .",
|
||||||
"test:watch": "jest --watch"
|
"test:watch": "jest --watch"
|
||||||
},
|
},
|
||||||
"author": "Jakub Synowiec <jsynowiec@users.noreply.github.com>",
|
"author": "Jakub Synowiec <jsynowiec@users.noreply.github.com>",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"body-parser": "^1.20.2",
|
"body-parser": "^1.20.2",
|
||||||
"elliptic": "^6.5.4",
|
"eckey-utils": "^0.7.13",
|
||||||
"express": "^4.18.2",
|
"elliptic": "^6.5.4",
|
||||||
"http_ece": "^1.1.0",
|
"express": "^4.18.2",
|
||||||
"jsonwebtoken": "^9.0.1",
|
"http_ece": "^1.1.0",
|
||||||
"node-fetch": "^3.3.2",
|
"jsonwebtoken": "^9.0.1",
|
||||||
"npm-check-updates": "16.11.1",
|
"node-fetch": "^3.3.2",
|
||||||
"reflect-metadata": "^0.1.13",
|
"npm-check-updates": "16.11.1",
|
||||||
"sqlite3": "^5.1.6",
|
"reflect-metadata": "^0.1.13",
|
||||||
"tslib": "~2.6",
|
"sqlite3": "^5.1.6",
|
||||||
"typeorm": "^0.3.17"
|
"tslib": "~2.6",
|
||||||
|
"typeorm": "^0.3.17"
|
||||||
},
|
},
|
||||||
"volta": {
|
"volta": {
|
||||||
"node": "18.12.1"
|
"node": "18.12.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ class Server {
|
|||||||
this.app.post('/web-push/subscribe', async (req: Request, res: Response) => {
|
this.app.post('/web-push/subscribe', async (req: Request, res: Response) => {
|
||||||
const subscription = req.body as BrowserSubscription;
|
const subscription = req.body as BrowserSubscription;
|
||||||
const message = { "title": "You are subscribed." } as Message;
|
const message = { "title": "You are subscribed." } as Message;
|
||||||
console.log("/web-push/subscribe", req.body);
|
|
||||||
await this.subscriptionService.addSubscription(subscription);
|
await this.subscriptionService.addSubscription(subscription);
|
||||||
await this.notificationService.sendNotification(subscription, message);
|
await this.notificationService.sendNotification(subscription, message);
|
||||||
res.status(201).send();
|
res.status(201).send();
|
||||||
|
|||||||
@@ -42,11 +42,13 @@ export class NotificationService {
|
|||||||
private async pushToEndpoint(subscription: BrowserSubscription, message: Message): Promise<void> {
|
private async pushToEndpoint(subscription: BrowserSubscription, message: Message): Promise<void> {
|
||||||
const payloadString = JSON.stringify(message);
|
const payloadString = JSON.stringify(message);
|
||||||
const payloadBuffer = Buffer.from(payloadString, 'utf-8');
|
const payloadBuffer = Buffer.from(payloadString, 'utf-8');
|
||||||
|
const vapidKeys: VapidKeys[] = await this.vapidService.getVapidKeys();
|
||||||
|
const vapidkey: VapidKeys = vapidKeys[0];
|
||||||
|
|
||||||
const encrypted = await this.encrypt(subscription.keys.p256dh, subscription.keys.auth, payloadBuffer);
|
const encrypted = await this.encrypt(vapidkey, subscription.keys.p256dh, subscription.keys.auth, payloadBuffer);
|
||||||
const endpoint = subscription.endpoint;
|
const endpoint = subscription.endpoint;
|
||||||
|
|
||||||
const vapidHeaders = await this.vapidService.createVapidAuthHeader(endpoint, 12 * 60 * 60, 'mailto:example@example.com');
|
const vapidHeaders = await this.vapidService.createVapidAuthHeader(endpoint, 12 * 60 * 60, 'mailto:example@example.com', vapidkey);
|
||||||
|
|
||||||
const parsedUrl = new URL(subscription.endpoint);
|
const parsedUrl = new URL(subscription.endpoint);
|
||||||
const options: https.RequestOptions = {
|
const options: https.RequestOptions = {
|
||||||
@@ -63,6 +65,9 @@ export class NotificationService {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
console.log(parsedUrl);
|
||||||
|
console.log(options);
|
||||||
|
|
||||||
return new Promise<void>((resolve, reject) => {
|
return new Promise<void>((resolve, reject) => {
|
||||||
const req = https.request(options, (res) => {
|
const req = https.request(options, (res) => {
|
||||||
if (res.statusCode! >= 200 && res.statusCode! < 300) {
|
if (res.statusCode! >= 200 && res.statusCode! < 300) {
|
||||||
@@ -82,34 +87,22 @@ export class NotificationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private async encrypt(p256dh: string, auth: string, payload: Buffer): Promise<Buffer> {
|
private async encrypt(appKeys: VapidKeys, p256dh: string, auth: string, payload: Buffer): Promise<Buffer> {
|
||||||
try {
|
try {
|
||||||
console.log('Public Key:', p256dh);
|
const vapidPrivateKeyBase64: string = appKeys['privateKey'];
|
||||||
console.log('Auth:', auth);
|
|
||||||
|
|
||||||
const vapidKeys: VapidKeys[] = await this.vapidService.getVapidKeys();
|
|
||||||
const vapidkey: VapidKeys = vapidKeys[0];
|
|
||||||
const vapidPrivateKeyBase64: string = vapidkey['privateKey'];
|
|
||||||
|
|
||||||
console.log("vapidPrivateKeyBase64: ", vapidPrivateKeyBase64);
|
|
||||||
|
|
||||||
const vapidPrivateKeyBuffer: Buffer = Buffer.from(vapidPrivateKeyBase64, 'base64');
|
const vapidPrivateKeyBuffer: Buffer = Buffer.from(vapidPrivateKeyBase64, 'base64');
|
||||||
const ecdh = crypto.createECDH('prime256v1');
|
const ecdh = crypto.createECDH('prime256v1');
|
||||||
ecdh.setPrivateKey(vapidPrivateKeyBuffer);
|
ecdh.setPrivateKey(vapidPrivateKeyBuffer);
|
||||||
const publicKeyBuffer: Buffer = Buffer.from(p256dh, 'base64');
|
const publicKeyBuffer: Buffer = Buffer.from(p256dh, 'base64');
|
||||||
|
|
||||||
console.log("1: ", payload);
|
|
||||||
console.log("3: ", publicKeyBuffer);
|
|
||||||
console.log("4: ", auth);
|
|
||||||
|
|
||||||
return http_ece.encrypt(payload, {
|
return http_ece.encrypt(payload, {
|
||||||
'privateKey': ecdh,
|
'privateKey': ecdh,
|
||||||
'dh': publicKeyBuffer,
|
'dh': publicKeyBuffer,
|
||||||
'authSecret': Buffer.from(auth)
|
'authSecret': Buffer.from(auth)
|
||||||
});
|
});
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error encrypting payload:', error);
|
console.error('Error encrypting payload:', error);
|
||||||
// Handle the error as appropriate for your application
|
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
|
import ecKeyUtils from "eckey-utils";
|
||||||
import { VapidKeys } from './VapidKeys.js';
|
import { VapidKeys } from './VapidKeys.js';
|
||||||
import jwt from 'jsonwebtoken';
|
import jwt from 'jsonwebtoken';
|
||||||
import crypto from 'crypto';
|
import crypto from 'crypto';
|
||||||
@@ -16,7 +16,6 @@ class VapidService {
|
|||||||
private static instance: VapidService;
|
private static instance: VapidService;
|
||||||
private dbService: DBService = DBService.getInstance();
|
private dbService: DBService = DBService.getInstance();
|
||||||
|
|
||||||
|
|
||||||
private constructor() {
|
private constructor() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,16 +59,22 @@ class VapidService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async createVapidAuthHeader(endpoint: string, expiration: number, subject: string): Promise<{ 'Authorization': string, 'Crypto-Key': string }> {
|
async createVapidAuthHeader(endpoint: string, expiration: number, subject: string, appKeys: VapidKeys): Promise<{ 'Authorization': string, 'Crypto-Key': string }> {
|
||||||
const { publicKey, privateKey } = await this.getVapidKeys()[0];
|
const { publicKey, privateKey } = appKeys;
|
||||||
|
|
||||||
const jwtInfo = {
|
const jwtInfo = {
|
||||||
aud: new URL(endpoint).origin,
|
aud: new URL(endpoint).origin,
|
||||||
exp: Math.floor((Date.now() / 1000) + expiration),
|
exp: Math.floor((Date.now() / 1000) + expiration),
|
||||||
sub: subject
|
sub: subject
|
||||||
};
|
};
|
||||||
|
const curveName = 'prime256v1';
|
||||||
|
const ecdh = crypto.createECDH(curveName);
|
||||||
|
const privateKeyBuffer = Buffer.from(privateKey, 'base64');
|
||||||
|
ecdh.setPrivateKey(privateKeyBuffer);
|
||||||
|
|
||||||
const jwtToken = jwt.sign(jwtInfo, privateKey, { algorithm: 'ES256' });
|
const pems = ecKeyUtils.generatePem({curveName, privateKey: ecdh.getPrivateKey(), publicKey: ecdh.getPublicKey() });
|
||||||
|
|
||||||
|
const jwtToken = jwt.sign(jwtInfo, pems.privateKey, { algorithm: 'ES256' });
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'Authorization': `vapid t=${jwtToken}, k=${publicKey}`,
|
'Authorization': `vapid t=${jwtToken}, k=${publicKey}`,
|
||||||
|
|||||||
Reference in New Issue
Block a user