<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Pradip's blog]]></title><description><![CDATA[I'm a full-stack web developer. Next.js lover, security freak.]]></description><link>https://blog.thepradipvc.com</link><generator>RSS for Node</generator><lastBuildDate>Wed, 15 Apr 2026 21:37:38 GMT</lastBuildDate><atom:link href="https://blog.thepradipvc.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[What is Web Push Protocol?]]></title><description><![CDATA[Web Push Protocol is a protocol that is to be followed while sending Push Message requests by anyone who is implementing Web Push Notifications on their website. We don't need to handle every aspect of WPP requests as in we don't need to create raw n...]]></description><link>https://blog.thepradipvc.com/what-is-web-push-protocol</link><guid isPermaLink="true">https://blog.thepradipvc.com/what-is-web-push-protocol</guid><category><![CDATA[web push protocol]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[push notifications]]></category><category><![CDATA[messaging]]></category><category><![CDATA[web-push]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[Cryptography]]></category><dc:creator><![CDATA[Pradip Chaudhary]]></dc:creator><pubDate>Sun, 07 Jul 2024 07:53:59 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1719925989703/915d0e92-5ea7-4ffd-969a-1490051f7fef.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Web Push Protocol is a protocol that is to be followed while sending Push Message requests by anyone who is implementing <a target="_blank" href="https://thepradipvc.hashnode.dev/what-are-web-push-notifications-and-how-do-they-work"><strong>Web Push Notifications</strong></a> on their website. We don't need to handle every aspect of WPP requests as in we don't need to create raw network requests ourselves. There are plenty of libraries that handle it for us like <a target="_blank" href="https://github.com/web-push-libs/"><strong>web-push-libs</strong></a>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1719922964813/ec41be25-e12c-4f93-ba64-8ce5dbf8fa3f.png" alt class="image--center mx-auto" /></p>
<p>But it's good to know what they are doing. They make the network requests while ensuring they are in the right format. The spec that defines this network request is <a target="_blank" href="https://datatracker.ietf.org/doc/html/draft-ietf-webpush-protocol"><strong>Web Push Protocol</strong></a>.</p>
<p>Let's see in detail what these libraries do and how all the components involved in this process maintain the authenticity and integrity of the Push Messages.</p>
<h2 id="heading-application-server-keys">Application Server Keys</h2>
<p>When we try to subscribe a user for the Push Notifications, we have to pass an <code>applicationServerKey</code> to the subscribe function. The browser forwards this key to the Push service which later uses it to verify that the same application which subscribed the user is triggering the push messages.</p>
<p>The Push Service verifies the authenticity of the push message through some headers that we need to set in our request which are defined by the <a target="_blank" href="https://datatracker.ietf.org/doc/html/draft-thomson-webpush-vapid">VAPID spec</a>.</p>
<p>Below I've mentioned the steps of how everything works.</p>
<ol>
<li><p>We need to sign some JSON information with our <strong>private application key</strong> on our server.</p>
</li>
<li><p>Then we send this signed information as a header in a POST request for Push Message.</p>
</li>
<li><p>The Push Service verifies that the Push Message is sent by us using our public key to check the received information is signed by our private key (relating to that public key). The public key is the same <strong>applicationServerKey</strong> we pass to the subscribe call from our website.</p>
</li>
<li><p>The Push Service sends the push message to the user if the signed information is valid.</p>
</li>
</ol>
<p>Here is an example of this flow of information.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1719924501188/ec4bd3c3-c274-4db9-81c6-dc24d56d0232.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-json-web-token-authentication">JSON web token (Authentication)</h2>
<p><a target="_blank" href="https://jwt.io">JSON web tokens</a> (JWTs) are used to send a message to any third party such that the receiver can validate who sent it. The "signed information" we add to a header in the request is a JSON web token.</p>
<p>The image below shows an example of the JSON web token. It is just a string, though we can think of it as three strings separated by dots. However, the actual JWTs are way longer, this is just for the visualization.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1719926376413/6c0aa6a7-c270-4b5a-8280-6976e8bdcec1.png" alt class="image--center mx-auto" /></p>
<p>The first and second strings (the JWT info and JWT data) are JSON objects that are base64 encoded. This means that anyone can read them by decoding them.</p>
<h3 id="heading-the-first-part-of-the-jwt-string">The first part of the JWT string</h3>
<p>The first string is the information about the JWT, it indicates which algorithm was used to create the signature (the third string which we'll discuss later).</p>
<p>The JWT info for web push must contain the following information:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"typ"</span>: <span class="hljs-string">"JWT"</span>,
  <span class="hljs-attr">"alg"</span>: <span class="hljs-string">"ES256"</span>
}
</code></pre>
<h3 id="heading-the-second-part-of-the-jwt-string">The second part of the JWT string</h3>
<p>The second string is the JWT Data. This has the information about the sender of the JWT, who it's intended for, and how long it's valid.</p>
<p>For web push, the JWT data would have this format:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"aud"</span>: <span class="hljs-string">"https://some-push-service.org"</span>,
  <span class="hljs-attr">"exp"</span>: <span class="hljs-string">"1469618703"</span>,
  <span class="hljs-attr">"sub"</span>: <span class="hljs-string">"mailto:example@web-push-book.org"</span>
}
</code></pre>
<p>The <code>aud</code> value is the "audience", meaning who the JWT is for. For web push requests the audience is the Push Service, so we set it to the <strong>origin of the Push Service.</strong></p>
<p>The <code>exp</code> is the expiration of the JWT. The expiration is a timestamp in seconds and we should not keep it longer than 24 hours to prevent snoopers from being able to re-use it if they somehow intercept it.</p>
<p>Finally, the <code>sub</code> value needs to be a URL or a mailto email address so that the push service can contact the sender if it needs to. (This is why the web-push library also requires an email address)</p>
<h3 id="heading-the-third-part-of-the-jwt-string">The third part of the JWT string</h3>
<p>The third string, the signature, is the result of taking the first two strings (the JWT info and JWT data), joining them with a dot character, which we'll call the "unsigned token", and signing it.</p>
<p>The signature is obtained by encrypting the "unsigned token" using ES256. According to the <a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc7519">JWT spec</a>, ES256 is short for "ECDSA using the P-256 curve and the SHA-256 hash algorithm".</p>
<p>A Push Service can decrypt the signature using the public application server key and verify if it's the same as the "unsigned token" (the first two strings in the JWT) to validate the request it receives.</p>
<p>The signed JWT (the three strings concatenated with dots) is included in the Authorization header, prefixed by "WebPush", and sent to the Web Push service, like this:</p>
<pre><code class="lang-http"><span class="hljs-attribute">Authorization</span>: 'WebPush [JWT Info].[JWT Data].[Signature]';
</code></pre>
<p>The Web Push Protocol also states that the public application server key should be sent in the Crypto-Key header as a URL-safe base64 encoded string with <code>p256ecdsa</code> prefix.</p>
<pre><code class="lang-http"><span class="hljs-attribute">Crypto-Key</span>: p256ecdsa=[URL Safe Base64 Public Application Server Key]
</code></pre>
<h2 id="heading-the-payload-encryption">The Payload Encryption</h2>
<p>Now that we've covered authentication, let's dive into payload encryption. You might wonder why we need to encrypt the payload for web push when other push services don't. Well, it's all about maintaining user privacy across different push services.</p>
<p>With web push, developers don't need to worry about which push service they're using. We just follow the protocol, and our message gets sent. But this convenience comes with a catch - we could potentially send messages through an untrustworthy push service. By encrypting the payload, we ensure that only the user's browser can read the data.</p>
<p>The encryption of the payload is defined in the <a target="_blank" href="https://tools.ietf.org/html/draft-ietf-webpush-encryption">Message Encryption Spec</a>.</p>
<p>In the below breakdown, I have simply provided the steps for encrypting the payload. I'm not going into the details of various techniques used like ECDH (Elliptic Curve Diffie-Hellman key exchange) and HKDF (HMAC-based Key Derivation Function) etc.</p>
<p>So let's break down the encryption process:</p>
<ol>
<li><p>Inputs: We need three things to start - the payload itself, the auth secret, and the p256dh key that we got from the PushSubscription.</p>
</li>
<li><p>Salt and Keys: We generate a random 16-byte salt and create a new set of public/private keys just for this encryption. These are separate from our application server keys.</p>
</li>
<li><p>Shared Secret: Using some fancy crypto magic (ECDH, for the curious), we create a shared secret using the subscription's public key and our new private key.</p>
</li>
<li><p>Pseudo Random Key (PRK): We combine the auth secret and shared secret to create a PRK. This adds an extra layer of security.</p>
</li>
<li><p>Content Encryption Key (CEK) and Nonce: These are derived from the PRK and some additional info. The CEK is what we'll use to actually encrypt our payload.</p>
</li>
<li><p>Encryption: Finally, we encrypt our payload using the CEK and nonce. We also add some padding to prevent eavesdroppers from guessing message types based on size.</p>
</li>
</ol>
<p>Once we've got our encrypted payload, we need to package it up with the right headers:</p>
<ol>
<li><p>Encryption Header: This contains the salt used for encrypting the payload.</p>
<pre><code class="lang-http"> Encryption: salt=[URL Safe Base64 Encoded Salt]
</code></pre>
</li>
<li><p>Crypto-Key Header: This contains our local public key (used for this specific encryption) and our application server public key which we added earlier.</p>
<pre><code class="lang-http"> Crypto-Key: dh=[URL Safe Base64 Encoded Local Public Key String]; p256ecdsa=[URL Safe Base64 Encoded Public Application Server Key]
</code></pre>
</li>
<li><p>Content Headers: These specify the length, type, and encoding of our payload.</p>
<pre><code class="lang-http"> Content-Length: [Number of Bytes in Encrypted Payload]
 Content-Type: 'application/octet-stream'
 Content-Encoding: 'aesgcm'
</code></pre>
</li>
</ol>
<p>We also have a few optional headers that can control how our push message is handled:</p>
<ul>
<li><p>TTL (Time to Live): This tells the push service how long to keep trying to deliver the message.</p>
<pre><code class="lang-http">  TTL: [Time to live in seconds]
</code></pre>
<p>  If you set a <code>TTL</code> of zero, the push service will attempt to deliver the message immediately, <strong>but</strong> if the device can't be reached, your message will be immediately dropped from the push service queue.</p>
</li>
<li><p>Topic: Useful for replacing older messages with newer ones.</p>
</li>
<li><p>Urgency: Helps push services manage battery life by prioritizing messages.</p>
<pre><code class="lang-http">  Urgency: [very-low | low | normal | high]
</code></pre>
</li>
</ul>
<p>With all this setup, we make a POST request to the endpoint specified in the PushSubscription. The push service will then handle delivering our message to the user's device.</p>
<h2 id="heading-response-from-push-service">Response from Push Service</h2>
<p>After sending our push message, we need to check the response from the push service. The status codes tell us if everything went smoothly or if we need to take action:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Status Code</td><td>Description</td></tr>
</thead>
<tbody>
<tr>
<td>201</td><td>Created. The request to send a push message was received and accepted.</td></tr>
<tr>
<td>429</td><td>Too many requests. This means your application server has reached a rate limit with a push service. The push service should include a 'Retry-After' header to indicate how long before another request can be made.</td></tr>
<tr>
<td>400</td><td>Invalid request. This generally means one of your headers is invalid or improperly formatted.</td></tr>
<tr>
<td>404</td><td>Not Found. This is an indication that the subscription is expired and can't be used. In this case, you should delete the `PushSubscription` and wait for the client to resubscribe the user.</td></tr>
<tr>
<td>410</td><td>Gone. The subscription is no longer valid and should be removed from the application server. This can be reproduced by calling `unsubscribe()` on a `PushSubscription`.</td></tr>
<tr>
<td>413</td><td>The payload size is too large. The minimum size payload a push service must support is <a target="_blank" href="https://tools.ietf.org/html/draft-ietf-webpush-protocol-10#section-7.2">4096 bytes</a> (or 4kb).</td></tr>
</tbody>
</table>
</div><p>By following this protocol, we ensure that our push messages are secure, authenticated, and properly handled by push services. It might seem like a lot, but remember - most of this complexity is handled by libraries. As developers, we just need to understand the basics to use these tools effectively.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>And there you have it - the Web Push Protocol in a nutshell. It may seem complex, but this intricacy is what makes web push notifications secure and universal across different browsers and devices.</p>
<p>While most of us won't implement this from scratch, understanding the process helps us use push notifications more effectively and troubleshoot issues when they arise. It's a perfect example of how modern web technologies balance user convenience with robust security.</p>
<p>So next time you send a push notification, you'll know exactly what's happening behind the scenes. Happy pushing!</p>
]]></content:encoded></item><item><title><![CDATA[What are Web Push Notifications? And How do they work?]]></title><description><![CDATA[Introduction
In this article, I will answer some prominent questions on web push notifications.
Web Push Notifications (aka Browser Push Notifications) is a way to send some message to users even when they are not actively using the website. This pro...]]></description><link>https://blog.thepradipvc.com/what-are-web-push-notifications-and-how-do-they-work</link><guid isPermaLink="true">https://blog.thepradipvc.com/what-are-web-push-notifications-and-how-do-they-work</guid><category><![CDATA[Web Development]]></category><category><![CDATA[push notifications]]></category><category><![CDATA[messaging]]></category><category><![CDATA[web-push]]></category><category><![CDATA[Service Workers]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Pradip Chaudhary]]></dc:creator><pubDate>Sun, 19 May 2024 03:30:23 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1716087936816/6fa7b5ca-7021-435f-97a2-a6fa4030f0d9.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>In this article, I will answer some prominent questions on web push notifications.</p>
<p>Web Push Notifications (aka Browser Push Notifications) is a way to send some message to users even when they are not actively using the website. This provides a very convenient way for website owners to increase user engagement on the website.</p>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">However, it should be used conscientiously as overuse of Push Notifications might annoy your users and they will perceive it as spam.</div>
</div>

<p>Web push notifications are shown to the user differently based on what platform, device, and browser the user is using.</p>
<p>Push Messages and Notifications are two separate but complementary browser technologies that work hand in hand to enable Web Push Notifications for websites. Push messages are a way for your server to send messages to the users' browsers. The notifications are what display that message to the user. It's possible to show the notifications without receiving the push message when the user is using your website. However, browsers currently don't support sending just a push message (silent push) to the user. You have to show the notification for the message you are sending to the user.</p>
<p>First, let's learn how Web Push Notifications differ from App Push Notifications. They drive people people crazy when they don't work as per people's expectations.</p>
<h2 id="heading-difference-between-web-push-notifications-and-app-push-notifications">Difference between Web Push Notifications and App Push Notifications</h2>
<p>Apps use their own or any third-party Push Notifications server which maintains the connection with their app on users' devices and can send Push Notifications. The user receives a notification for the apps even when the app is closed.</p>
<p>However, our website doesn't have the power to keep a connection with any server when they are closed. So, it has few limitations as compared to apps. For the websites, the browser companies maintain a Push Service or may use any other third-party push service. The website needs to send the notification that they want to show to users to these Push Services through Web Push Protocol. I will explain the Web Push Protocol in detail in the next article of this series.</p>
<h2 id="heading-how-do-web-push-notifications-work">How do Web Push Notifications work?</h2>
<p>At a high level, the key steps for implementing push notifications are:</p>
<ol>
<li><p>Adding frontend logic to ask the user for permission to send push notifications and then sending the unique client identifier to the server for storage.</p>
</li>
<li><p>Adding backend logic to send push messages to the user through the browser's push service.</p>
</li>
<li><p>Adding frontend logic to receive messages and display them as notifications.</p>
</li>
</ol>
<p>I will create a complete code guide for sending push notifications in the upcoming articles. For now, let's understand step-by-step the overview of how push notifications are sent.</p>
<h3 id="heading-get-permission-to-send-push-notifications">Get permission to send push notifications</h3>
<p>First, your website needs a user's permission to send push notifications. To get the user notification, you need to call the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Notification/requestPermission_static"><code>Notification.requestPermission()</code></a> function which will prompt the user for permission. However, this function should only be called on some user actions like a button click (which is also called the soft ask method) because most browsers don't allow certain actions without any user interaction and the notifications permission is one of them.</p>
<h3 id="heading-subscribe-the-client-to-push-notifications">Subscribe the client to push notifications</h3>
<p>After getting the permission, your website needs to subscribe the user for the push notifications. This is done through the <a target="_blank" href="https://developer.mozilla.org/docs/Web/API/Push_API">Push API</a> of browsers. You'll need to provide a public key during the subscription process (more on that later). The browser will then contact the push service for the subscription and return a <a target="_blank" href="https://developer.mozilla.org/docs/Web/API/PushSubscription">PushSubscription</a> object. This object data has to be stored by your server to send the push notifications afterward.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1715926220224/8fb9b7bb-9e17-4ded-90cc-ff4ed7264e4a.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-send-a-push-message">Send a push message</h3>
<p>Your server doesn't directly send messages to the user. It has to send the message to the push service which is controlled by the users' browser vendor. The request that your server sends to the push service is called a web push protocol request. It should include:</p>
<ul>
<li><p>what is the data to be sent</p>
</li>
<li><p>to which user the data has to be sent</p>
</li>
<li><p>Instructions to the push service on how it should deliver the push message. For example, you can specify that the push service should stop attempting to send the message after 10 minutes.</p>
</li>
</ul>
<p>Your server doesn't have to construct the raw web push request itself. Some libraries can handle that for you, such as the <a target="_blank" href="https://github.com/web-push-libs/">web-push-libs</a>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1715927520214/c52d9554-7773-4bc7-bef1-68c34315df1d.png" alt class="image--center mx-auto" /></p>
<p>The push service receives the request, authenticates it, and then sends it to the appropriate client (user). If the client's browser is offline, the push service will queue the message and wait for the browser to come online.</p>
<p>You need to make sure you send the push message request to the correct push service (this is also taken care of by web-push-libs). The <code>PushSubscription</code> data that the browser returned to you during the subscription process provides this information. This is what a PushSubscription object looks like:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"endpoint"</span>: <span class="hljs-string">"https://fcm.googleapis.com/fcm/send/c1KrmpTuRm…"</span>,
  <span class="hljs-attr">"expirationTime"</span>: <span class="hljs-literal">null</span>,
  <span class="hljs-attr">"keys"</span>: {
    <span class="hljs-attr">"p256dh"</span>: <span class="hljs-string">"BGyyVt9FFV…"</span>,
    <span class="hljs-attr">"auth"</span>: <span class="hljs-string">"R9sidzkcdf…"</span>
  }
}
</code></pre>
<p>The <code>domain</code> of the endpoint is essentially the push service, and the endpoint's path is the client identifier using which the push service determines which client to send the message. The keys are used for encryption.</p>
<h4 id="heading-encrypt-the-push-message">Encrypt the push message</h4>
<p>The data that you send to the push service must be encrypted using the keys provided in the <code>PushSubscription</code>. It prevents the push service from reading the data. The browser might be using a third-party push service which can be unsafe or insecure. So it's best to encrypt your messages.</p>
<h4 id="heading-sign-your-web-push-protocol-requests">Sign your web push protocol requests</h4>
<p>So, now what prevents others who get to know your users' <code>PushSubscription</code>'s endpoint. That's where signing a request comes into the picture. This is only necessary in Chrome right now but other browsers may also require this in the future.</p>
<p>This authentication works as follows:</p>
<ol>
<li><p>You generate a public and private key once for your server. These are also called the <strong>application server keys.</strong> You might also see them referred to as VAPID keys. <a target="_blank" href="https://tools.ietf.org/html/draft-thomson-webpush-vapid-02">VAPID</a> is the spec that defines this authentication process.</p>
</li>
<li><p>Every time you subscribe a client to push notifications, you provide your public key. The push service associates this public key with the endpoint it generated for the device.</p>
</li>
<li><p>When you send a web push protocol request, you sign some JSON data with your private key.</p>
</li>
<li><p>When the push service receives the request, it uses the stored public key to authenticate the signed information. If the signature is valid then the push service knows that it has indeed come from your server (Make sure your Private key is not leaked otherwise anyone can sign requests on your behalf).</p>
</li>
</ol>
<h4 id="heading-customize-the-delivery-of-the-push-message">Customize the delivery of the push message</h4>
<p>The web push protocol request specification also includes parameters that allow you to customize the push service's message delivery attempts to the client. For instance, you can modify:</p>
<ul>
<li><p>The Time-To-Live (TTL) of a message, indicating how long the push service should try to deliver a message.</p>
</li>
<li><p>The message urgency, which is beneficial if the push service is conserving the client's battery life by only delivering high-priority messages.</p>
</li>
<li><p>The message topic, which replaces any pending messages of the same topic with the most recent message.</p>
</li>
</ul>
<h3 id="heading-receive-and-display-the-pushed-messages-as-notifications">Receive and display the pushed messages as notifications</h3>
<p>The push service delivers the message to the user as soon as he is online. When a client's browser receives the message, it decrypts the message and dispatches a push event to your <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorker">service worker</a>. A service worker is just a javascript code that can run in the background even when your website is not open.</p>
<p>In the push event handler of your service worker, you need to call <code>ServiceWorkerRegistration.showNotification()</code> to display the message as a notification. The <code>SeviceWorkerRegistration</code> can be accessed through <code>self.registration</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1716083292567/1edd5765-24c5-4124-b164-4e39d742caaf.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>We've covered a lot of ground in understanding how web push notifications work under the hood. From getting permission to subscribing the client, encrypting the message, authenticating requests, customizing delivery, and finally handling the push event to display the notification - it's a multi-step process that might seem daunting at first.</p>
<p>But don't worry, in the upcoming articles, I'll provide a complete code walkthrough to implement web push notifications step-by-step. We'll dive into the nitty-gritty details, explore some helpful libraries, and get our hands dirty with code examples.</p>
<p>By the end, you'll be a web push notification ninja, ready to engage your users even when they're not actively browsing your website. Just remember to use this power responsibly and avoid going overboard with spam notifications that'll send your users running for the hills.</p>
<p>So stay tuned, grab a cup of coffee (or beverage of choice), and get ready to level up your web game with the magical world of push notifications!</p>
]]></content:encoded></item></channel></rss>