1. Prompt and scope
An e-commerce Web Push notification should open an order detail. The app may be closed, an existing window may be open, or the user may activate an action. Design the deep link with the navigate option and cover URL parsing, permissions, click fallback, duplicate openings, and Service Worker lifetime.
2. What the interviewer is testing
- Know that
NotificationOptions.navigateis a navigation URL andNotification.navigateexposes its parsed absolute URL or an empty string. - Distinguish default notification navigation, action navigation, and
notificationclickfallback handling. - Handle permission, HTTPS, same-origin policy, open redirects, duplicate notifications, and existing-window reuse.
- Keep route recovery, authentication, and idempotency in the application layer rather than treating browser navigation as authorization.
3. Questions to clarify first
- Does page code or a Service Worker call
showNotification? - Are external URLs allowed, and how should an authenticated order page resume after sign-in?
- Should body clicks and action clicks open different routes or perform different operations?
- When a same-origin window exists, should the product focus it or create another window?
4. Thirty-second answer
Under HTTPS, I would have the Service Worker create a persistent notification whose navigate URL passes a same-origin allowlist. A body or action with a navigation URL can be handled by the browser; an action without one falls back to notificationclick, where I focus an existing window or open a recovery route. Permission, invalid URLs, and authentication failures still need explicit application handling.
5. Step-by-step deep dive
Step 1: Build a validated notification URL
const target = new URL(`/orders/${orderId}`, self.location.origin);
await self.registration.showNotification("Order shipped", {
body: "View tracking details",
tag: `order-${orderId}`,
navigate: target.href,
data: { orderId },
});navigate is resolved against the base URL used when the notification is created. Server-provided paths must pass same-origin and route-allowlist checks; arbitrary user input must never become a redirect target.
Step 2: Separate body and action navigation
await self.registration.showNotification("Order needs confirmation", {
body: "Choose an action",
navigate: "/orders/123",
actions: [
{ action: "open", title: "View order", navigate: "/orders/123" },
{ action: "help", title: "Contact support" },
],
});When an action is activated, its own navigate takes precedence. An action without that URL enters notificationclick, where application-specific behavior runs. Body and action routes should share the same allowlist and authentication recovery rules.
Step 3: Handle click fallback and existing windows
self.addEventListener("notificationclick", (event) => {
event.notification.close();
if (event.action === "help") {
event.waitUntil(clients.openWindow("/support"));
return;
}
event.waitUntil(clients.matchAll({ type: "window", includeUncontrolled: true })
.then((windows) => windows[0]?.focus() ?? clients.openWindow("/orders/123")));
});Production code should verify the window URL, wait for Service Worker control, and pass trusted data rather than hard-coding an order. Browsers may reuse or create a top-level window, so application recovery must support both outcomes.
Step 4: Permissions, protocol, and security
Notifications require user permission, and persistent notifications require a Service Worker; the APIs depend on a secure context. Permission is not business authorization: the order API must authenticate again after the page opens. Restrict navigation to same-origin or explicitly trusted external origins to avoid open redirects and phishing links.
Step 5: Restore state idempotently
After opening, read the route and data.orderId, show a loading state, then fetch the order and verify identity. Notifications with the same tag should be merged or updated according to product policy. Route changes, focus restoration, and analytics must be idempotent so one click cannot create duplicate requests or state transitions.
6. Model high-quality answer
I would let the Service Worker create only allowlisted same-originnavigateURLs and assign a stabletag. A body click follows browser navigation, while an action URL takes precedence; an action without one usesnotificationclickto focus an existing window or open a fallback. The page authenticates again, reads the order parameter, and restores state idempotently. Permission, HTTPS, open redirects, and external URLs are separate checks; notification navigation is never authorization.
7. Common mistakes
- Put user input directly in
navigate→ open redirect → enforce same-origin and route allowlists. - Assume every click reaches custom code → body navigation may be handled by the browser → rely on
notificationclickonly for actions without a URL. - Treat notification permission as order authorization → data exposure → authenticate again in the page and API.
- Always call
openWindow→ duplicate windows and requests → match same-origin windows and make recovery idempotent. - Ignore Service Worker lifetime → async work is interrupted → keep completion promises inside
event.waitUntil.
8. Follow-up questions
Follow-up 1: What does Notification.navigate return?
It is a read-only string containing the notification’s serialized absolute navigation URL, or an empty string when no valid URL was set.
Follow-up 2: What happens when an action has no navigate?
The action does not navigate automatically. Its activation can be handled by notificationclick in the Service Worker.
Follow-up 3: Why authenticate again in the page?
The URL and data are navigation hints, not identity or resource authorization. The order API must check the current session and server-side permissions again.
Follow-up 4: How do you test cross-window behavior?
Cover no window, an existing same-origin window, an uncontrolled window, body and action clicks, denied permission, and invalid URLs; assert that application recovery makes only one request.