In practice: PWA & Web Push (the browser subscribe side) · cheat sheet.
Rask.WebPush delivers a Web Push notification from your backend to a subscribed browser — signed with
your own VAPID keys (RFC 8292) and payload-encrypted with aes128gcm
(RFC 8291) — with zero external dependencies. It's a standalone
server library: no UI, no transport dependency, no reference to the rest of Rask, so it works from a plain
ASP.NET app, a Rask Server app, or the ASP.NET host of a WASM PWA. It pairs with the browser-side
IWebPush that produces the subscription.
dotnet add package Rask.WebPushA browser push subscription is useless until something pushes to it. The push service (FCM, Mozilla, …)
will only accept a message that is VAPID-signed by the application server that owns the subscription and
encrypted for that subscription's keys. Rask.WebPush does both and POSTs the result to the endpoint, so
you hand it a stored subscription and a message and get back a typed result telling you whether the
subscription is still good.
1. Generate a key pair once and store it in configuration/secrets (rotating it invalidates every existing subscription):
var keys = VapidKeys.Generate(); // dotnet-run once; persist keys.PublicKey / keys.PrivateKey2. Register the sender at startup:
builder.Services.AddRaskWebPush(o =>
{
o.VapidKeys = new VapidKeys(config["WebPush:PublicKey"]!, config["WebPush:PrivateKey"]!);
o.Subject = "mailto:admin@example.com"; // a contact the push service can reach; mailto: or https:
});3. Subscribe on the client with the same public key, and store what it posts up. Hand
keys.PublicKey to the browser's IWebPush.SubscribeAsync; the client
POSTs three fields — Endpoint, P256dh, Auth — which you persist as a PushSubscription.
4. Send a notification, and act on the result:
public sealed class Notifier(IWebPushSender sender, ISubscriptionStore store)
{
public async Task NotifyAsync(Guid userId, string title, string body, string url, CancellationToken ct)
{
foreach (var sub in await store.ForUserAsync(userId, ct))
{
var result = await sender.SendAsync(sub, WebPushMessage.Text(title, body, url), ct);
if (result.ShouldDelete) await store.RemoveAsync(sub, ct); // 404/410 — subscription is gone
else if (result.ShouldRetry) { /* 429/5xx — enqueue and try later (e.g. via Rask.Jobs) */ }
}
}
}VapidKeys— a base64url P-256 key pair.PublicKeyis exactly theapplicationServerKeythe browser passes topushManager.subscribe, so the same string goes to the client'sIWebPush.SubscribeAsync;PrivateKeystays secret on the server.VapidKeys.Generate()mints a fresh pair.PushSubscription(Endpoint, P256dh, Auth)— the server-side mirror of the browser's subscription: the push-service URL plus the client's ECDH public key and auth secret used to encrypt the payload. It's the package's own type (no dependency onRask.Wasm/Rask.Core); the two sides just agree on the wire shape.WebPushMessage— typed fields (Title,Body,Icon,Badge,Tag,Url) serialize to the JSON the default service worker (rask-sw.js) expects —{ title, body, icon, badge, tag, data: { url } }— so a push shows a notification with no service-worker changes.WebPushMessage.Text(title, body?, url?)is the common case;WebPushMessage.Raw(json)(or settingRawPayload) sends a hand-built payload verbatim for your own worker.Urgency(RFC 8030 §5.3),Ttl, andTopic(a ≤32-char collapse key) map to the corresponding push-service semantics.IWebPushSender.SendAsync— signs (VAPID), encrypts (aes128gcm), and POSTs via anIHttpClientFactorytyped client, returning aWebPushResult.WebPushResult— classifies the outcome so the caller knows what to do:IsSuccess,ShouldDelete(HTTP 404/410 — the subscription expired, remove it from your store),ShouldRetry(429/5xx — transient, retry later), or a permanent failure (usually a VAPID/config error — don't retry).
- Server-side and stateless.
Rask.WebPushsends; it does not store subscriptions. Persist thePushSubscriptionyour client posts up in whatever store you like (aRask.Dataentity, a table, …), keyed by user. A browser holds one subscription per service-worker registration, and a user may have several devices — store and iterate all of them. - Keep the keys stable. Generate one pair per application and reuse it for the app's lifetime; rotating the VAPID keys invalidates every subscription the old public key produced.
Subjectis required and must be amailto:address or anhttps:URL — the push service uses it to reach you if your traffic causes problems.AddRaskWebPushvalidates the options at startup, so a missing key or subject fails fast rather than on the first send.- Prune expired subscriptions. Act on
ShouldDeleteso your store doesn't accumulate dead endpoints, and consider running sends throughRask.Jobsso aShouldRetryresult is retried durably off the request thread. - The client half lives elsewhere. Requesting permission, subscribing, and handling the notification are
the browser's job — see PWA & Web Push for
IWebPushand the default service worker.