|
1 |
| -import type { PushNotificationSubscription } from "../types.js"; |
2 | 1 | import { toBase64Url } from "../utils/base64url.js";
|
3 | 2 |
|
| 3 | +type BaseOptions = { |
| 4 | + ttl?: number; |
| 5 | + urgency?: "very-low" | "low" | "normal" | "high"; |
| 6 | +}; |
| 7 | + |
| 8 | +type AESGCMOptions = BaseOptions & { |
| 9 | + algorithm: "aesgcm"; |
| 10 | + salt: ArrayBuffer; |
| 11 | + localPublicKey: CryptoKey; |
| 12 | +}; |
| 13 | + |
| 14 | +type AES128GCMOptions = BaseOptions & { |
| 15 | + algorithm?: "aes128gcm"; |
| 16 | +}; |
| 17 | + |
| 18 | +type EncryptionOptions = AESGCMOptions | AES128GCMOptions; |
| 19 | + |
4 | 20 | /**
|
5 | 21 | * Generate the headers for a Web Push request.
|
6 | 22 | * @param publicVapidKey - The public VAPID key.
|
7 | 23 | * @param jwt - The signed JWT token.
|
8 | 24 | * @param encryptedPayload - The encrypted payload.
|
9 |
| - * @param salt - The salt used to encrypt the payload. |
10 |
| - * @param localPublicKey - The public key used to encrypt the payload. |
11 |
| - * @param ttl - The time-to-live for the notification. |
| 25 | + * @param options - Options for encryption and additional headers. Defaults to AES128GCM if not specified. |
12 | 26 | * @returns The generated headers.
|
13 | 27 | */
|
14 | 28 | export async function generateHeaders(
|
15 | 29 | publicVapidKey: CryptoKey,
|
16 | 30 | jwt: string,
|
17 | 31 | encryptedPayload: ArrayBuffer,
|
18 |
| - salt: ArrayBuffer, |
19 |
| - localPublicKey: CryptoKey, |
20 |
| - ttl = 86400, |
21 |
| -) { |
| 32 | + options: EncryptionOptions = { algorithm: "aes128gcm" }, |
| 33 | +): Promise<Headers> { |
22 | 34 | const exportedPubKey = await crypto.subtle.exportKey("raw", publicVapidKey);
|
23 | 35 | const encodedPubKey = toBase64Url(exportedPubKey);
|
24 | 36 |
|
25 |
| - const exportedLocalPubKey = await crypto.subtle.exportKey( |
26 |
| - "raw", |
27 |
| - localPublicKey, |
28 |
| - ); |
29 |
| - const encodedLocalPubKey = toBase64Url(exportedLocalPubKey); |
30 |
| - |
31 | 37 | const headers = new Headers();
|
32 |
| - headers.append("Authorization", `Bearer ${jwt}`); |
33 |
| - const cryptoKey = new URLSearchParams(); |
34 |
| - cryptoKey.set("dh", encodedLocalPubKey); |
| 38 | + headers.append("Content-Type", "application/octet-stream"); |
| 39 | + headers.append("Content-Length", encryptedPayload.byteLength.toString()); |
| 40 | + headers.append("TTL", Math.floor(options.ttl ?? 86400).toString()); |
35 | 41 |
|
36 |
| - // On Microsoft Edge servers, this doesn't work, despite being documented in the spec |
37 |
| - // headers.append("Crypto-Key", `p256ecdsa=${encodedPubKey}`); |
38 |
| - // headers.append("Crypto-Key", `dh=${encodedLocalPubKey}`); |
| 42 | + if (options.urgency) { |
| 43 | + headers.append("Urgency", options.urgency); |
| 44 | + } |
39 | 45 |
|
40 |
| - // Also on Microsoft, the order matters, despite the spec saying it doesn't |
41 |
| - headers.append( |
42 |
| - "Crypto-Key", |
43 |
| - `p256ecdsa=${encodedPubKey};dh=${encodedLocalPubKey}`, |
44 |
| - ); |
| 46 | + if (options.algorithm === "aesgcm") { |
| 47 | + const exportedLocalPubKey = await crypto.subtle.exportKey( |
| 48 | + "raw", |
| 49 | + options.localPublicKey, |
| 50 | + ); |
| 51 | + const encodedLocalPubKey = toBase64Url(exportedLocalPubKey); |
45 | 52 |
|
46 |
| - headers.append("Content-Encoding", "aesgcm"); |
47 |
| - headers.append("Content-Type", "application/octet-stream"); |
48 |
| - headers.append("Content-Length", encryptedPayload.byteLength.toString()); |
49 |
| - headers.append("Encryption", `salt=${toBase64Url(salt)}`); |
50 |
| - headers.append("TTL", Math.floor(ttl).toString()); |
| 53 | + headers.append("Authorization", `Bearer ${jwt}`); |
| 54 | + headers.append("Content-Encoding", "aesgcm"); |
| 55 | + |
| 56 | + // On Microsoft Edge servers, this doesn't work, despite being documented in the spec |
| 57 | + // headers.append("Crypto-Key", `p256ecdsa=${encodedPubKey}`); |
| 58 | + // headers.append("Crypto-Key", `dh=${encodedLocalPubKey}`); |
| 59 | + headers.append( |
| 60 | + "Crypto-Key", |
| 61 | + `p256ecdsa=${encodedPubKey};dh=${encodedLocalPubKey}`, |
| 62 | + ); |
| 63 | + headers.append("Encryption", `salt=${toBase64Url(options.salt)}`); |
| 64 | + } else { |
| 65 | + headers.append("Authorization", `vapid t=${jwt}, k=${encodedPubKey}`); |
| 66 | + headers.append("Content-Encoding", "aes128gcm"); |
| 67 | + } |
51 | 68 |
|
52 | 69 | return headers;
|
53 | 70 | }
|
0 commit comments