You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

66 lines
1.9 KiB

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<VapidKeys> {
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
};
}
}