import { Database } from 'sqlite3'; import jwt from 'jsonwebtoken'; import crypto from 'crypto'; export interface VapidKeys { publicKey: string; privateKey: string; } export class VapidService { private db: Database; constructor() { this.db = new Database('vapidKeys.db'); this.initializeDatabase(); } private initializeDatabase(): void { this.db.run(`CREATE TABLE IF NOT EXISTS vapid (id INTEGER PRIMARY KEY, publicKey TEXT, privateKey TEXT)`); } private generateVAPIDKeys(): VapidKeys { const ecdh = crypto.createECDH('prime256v1'); ecdh.generateKeys(); return { publicKey: ecdh.getPublicKey().toString('base64'), privateKey: ecdh.getPrivateKey().toString('base64') }; } async getVapidKeys(): Promise { return new Promise((resolve, reject) => { this.db.get('SELECT publicKey, privateKey FROM vapid WHERE id = ?', [1], (err, row: VapidKeys) => { if (err) reject(err); if (row) { resolve({ publicKey: row.publicKey, privateKey: row.privateKey }); } else { const keys = this.generateVAPIDKeys(); this.db.run('INSERT INTO vapid (publicKey, privateKey) VALUES (?, ?)', [keys.publicKey, keys.privateKey], (err) => { if (err) reject(err); resolve(keys); }); } }); }); } async createVapidAuthHeader(endpoint: string, expiration: number, subject: string): Promise<{ 'Authorization': string, 'Crypto-Key': string }> { const { publicKey, privateKey } = await this.getVapidKeys(); const jwtInfo = { aud: new URL(endpoint).origin, exp: Math.floor((Date.now() / 1000) + expiration), sub: subject }; const jwtToken = jwt.sign(jwtInfo, privateKey, { algorithm: 'ES256' }); return { 'Authorization': `vapid t=${jwtToken}, k=${publicKey}`, 'Crypto-Key': publicKey }; } }