Skip to content

Commit 19fc88e

Browse files
authored
feat: improve VAPID config doc and type hints (web-push-libs#443)
Clarified VAPID requirements.
1 parent a368e42 commit 19fc88e

File tree

3 files changed

+45
-14
lines changed

3 files changed

+45
-14
lines changed

README.md

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,12 @@ $report = $webPush->sendOneNotification(
132132

133133
### Authentication (VAPID)
134134

135-
Browsers need to verify your identity. A standard called VAPID can authenticate you for all browsers. You'll need to create and provide a public and private key for your server. These keys must be safely stored and should not change.
135+
Browsers need to verify your identity. A standard called VAPID can authenticate you for all browsers based on [RFC8292](https://www.rfc-editor.org/rfc/rfc8292).
136+
You'll need to create and provide a public and private key for your server. These keys must be safely stored and should not change.
137+
138+
According to the standard it is optional to provide contact details by the `subject` property.
139+
In practice all browsers require a valid `subject` which can contain an email address or an available https website. It should not change.
140+
Please note that browser manufacturers may use additional verification methods to prevent abuse of the push service.
136141

137142
You can specify your authentication details when instantiating WebPush. The keys can be passed directly (recommended), or you can load a PEM file or its content:
138143

@@ -144,19 +149,27 @@ use Minishlink\WebPush\WebPush;
144149
$endpoint = 'https://fcm.googleapis.com/fcm/send/abcdef...'; // Chrome
145150

146151
$auth = [
147-
'VAPID' => [
148-
'subject' => 'mailto:me@website.com', // can be a mailto: or your website address
149-
'publicKey' => '~88 chars', // (recommended) uncompressed public key P-256 encoded in Base64-URL
150-
'privateKey' => '~44 chars', // (recommended) in fact the secret multiplier of the private key encoded in Base64-URL
151-
'pemFile' => 'path/to/pem', // if you have a PEM file and can link to it on your filesystem
152-
'pem' => 'pemFileContent', // if you have a PEM file and want to hardcode its content
152+
'VAPID' => [ // Recommended.
153+
'subject' => 'mailto:me@website.com', // Must be an email beginning with mailto: or available https website address.
154+
'publicKey' => '~88 chars', // Uncompressed public key P-256 encoded in Base64-URL.
155+
'privateKey' => '~44 chars', // In fact the secret multiplier of the private key encoded in Base64-URL.
156+
],
157+
'VAPID' => [ // Alternative 1.
158+
'subject' => 'mailto:me@website.com', // Must be an email beginning with mailto: or available https website address.
159+
'pemFile' => 'path/to/pem', // If you have a PEM file and can link to it on your filesystem.
160+
],
161+
'VAPID' => [ // Alternative 2.
162+
'subject' => 'mailto:me@website.com', // Must be an email beginning with mailto: or available https website address.
163+
'pem' => 'pemFileContent', // If you have a PEM file and want to hardcode its content.
153164
],
154165
];
155166

156167
$webPush = new WebPush($auth);
157168
$webPush->queueNotification(...);
158169
```
159170

171+
#### Create VAPID keys
172+
160173
In order to generate the uncompressed public and secret key, encoded in Base64, enter the following in your Linux bash:
161174

162175
```bash

phpstan.neon

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ parameters:
33
paths:
44
- src
55
reportUnmatchedIgnoredErrors: false
6+
treatPhpDocTypesAsCertain: false
67
ignoreErrors:
78
- identifier: missingType.iterableValue
89
strictRules:

src/VAPID.php

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,30 @@
1818
use Jose\Component\Signature\JWSBuilder;
1919
use Jose\Component\Signature\Serializer\CompactSerializer;
2020

21+
/**
22+
* @phpstan-type VapidConfig array{
23+
* subject: string,
24+
* publicKey: string, // ~88 chars Base64URL
25+
* privateKey: string // ~44 chars Base64URL
26+
* }
27+
* @phpstan-type VapidPemFileConfig array{
28+
* subject: string,
29+
* pemFile: string // path/to/pem
30+
* }
31+
* @phpstan-type VapidPemConfig array{
32+
* subject: string,
33+
* pem: string // PEM file content
34+
* }
35+
* @phpstan-type InputVapidConfig VapidConfig|VapidPemFileConfig|VapidPemConfig
36+
*/
2137
class VAPID
2238
{
2339
private const PUBLIC_KEY_LENGTH = 65;
2440
private const PRIVATE_KEY_LENGTH = 32;
2541

2642
/**
43+
* @param InputVapidConfig $vapid
44+
* @return VapidConfig
2745
* @throws \ErrorException
2846
*/
2947
public static function validate(array $vapid): array
@@ -36,19 +54,19 @@ public static function validate(array $vapid): array
3654
$vapid['pem'] = file_get_contents($vapid['pemFile']);
3755

3856
if (!$vapid['pem']) {
39-
throw new \ErrorException('Error loading PEM file.');
57+
throw new \ErrorException('[VAPID] Error loading PEM file.');
4058
}
4159
}
4260

4361
if (isset($vapid['pem'])) {
4462
$jwk = JWKFactory::createFromKey($vapid['pem']);
4563
if ($jwk->get('kty') !== 'EC' || !$jwk->has('d') || !$jwk->has('x') || !$jwk->has('y')) {
46-
throw new \ErrorException('Invalid PEM data.');
64+
throw new \ErrorException('[VAPID] Invalid PEM data.');
4765
}
4866

4967
$binaryPublicKey = hex2bin(Utils::serializePublicKeyFromJWK($jwk));
5068
if (!$binaryPublicKey) {
51-
throw new \ErrorException('Failed to convert VAPID public key from hexadecimal to binary');
69+
throw new \ErrorException('[VAPID] Failed to convert VAPID public key from hexadecimal to binary.');
5270
}
5371
$vapid['publicKey'] = base64_encode($binaryPublicKey);
5472
$vapid['privateKey'] = base64_encode(str_pad(Base64Url::decode($jwk->get('d')), self::PRIVATE_KEY_LENGTH, '0', STR_PAD_LEFT));
@@ -113,7 +131,6 @@ public static function getVapidHeaders(
113131
'alg' => 'ES256',
114132
];
115133

116-
117134
try {
118135
$jwtPayload = json_encode(
119136
[
@@ -161,7 +178,7 @@ public static function getVapidHeaders(
161178
}
162179

163180
// @phpstan-ignore deadCode.unreachable
164-
throw new \ErrorException('This content encoding is not supported');
181+
throw new \ErrorException('This content encoding is not supported.');
165182
}
166183

167184
/**
@@ -176,12 +193,12 @@ public static function createVapidKeys(): array
176193

177194
$binaryPublicKey = hex2bin(Utils::serializePublicKeyFromJWK($jwk));
178195
if (!$binaryPublicKey) {
179-
throw new \ErrorException('Failed to convert VAPID public key from hexadecimal to binary');
196+
throw new \ErrorException('Failed to convert VAPID public key from hexadecimal to binary.');
180197
}
181198

182199
$binaryPrivateKey = hex2bin(str_pad(bin2hex(Base64Url::decode($jwk->get('d'))), 2 * self::PRIVATE_KEY_LENGTH, '0', STR_PAD_LEFT));
183200
if (!$binaryPrivateKey) {
184-
throw new \ErrorException('Failed to convert VAPID private key from hexadecimal to binary');
201+
throw new \ErrorException('Failed to convert VAPID private key from hexadecimal to binary.');
185202
}
186203

187204
return [

0 commit comments

Comments
 (0)