fix(site): request notification permission before push subscription (#22576)

## Problem

The subscribe flow in `useWebpushNotifications` called
`pushManager.subscribe()` without first requesting the `Notification`
permission. When the browser permission state is `"denied"` (e.g. from a
previous prompt dismissal), the browser throws:

```
DOMException: Registration failed - permission denied
```

This surfaced as a confusing error toast on the agents page. The error
has nothing to do with Coder RBAC roles — it's the browser denying the
push subscription because notification permission was previously
declined. An admin who had granted browser permission wouldn't see this;
a user who previously dismissed or denied the prompt would.

## Fix

Added an explicit `Notification.requestPermission()` call before
`pushManager.subscribe()`. This:

1. **Re-prompts** the user if the permission state is `"default"` (not
yet decided)
2. **Throws a clear, actionable error** if the permission is `"denied"`:
*"Notifications are blocked by your browser. Please allow notifications
for this site in your browser settings."*
3. **Only proceeds** to `pushManager.subscribe()` after permission is
confirmed as `"granted"`

## Tests

New test file `useWebpushNotifications.jest.ts`:
- **requests notification permission before subscribing** — verifies
`requestPermission()` is called before `pushManager.subscribe()`
- **throws a clear error when permission is denied** — verifies the
user-friendly error message
- **does not call pushManager.subscribe when permission is denied** —
verifies we bail out early
This commit is contained in:
Kyle Carberry
2026-03-03 17:13:31 -05:00
committed by GitHub
parent 39bde165b8
commit 4ee5306eca
@@ -55,6 +55,19 @@ export const useWebpushNotifications = (): WebpushNotifications => {
const subscribe = async (): Promise<void> => {
try {
setLoading(true);
// Explicitly request notification permission before subscribing
// to the push manager. Without this, pushManager.subscribe()
// throws a generic "Registration failed - permission denied"
// DOMException when the permission state is "denied", which
// gives no opportunity to show a clear message to the user.
const permission = await Notification.requestPermission();
if (permission !== "granted") {
throw new Error(
"Notifications are blocked by your browser. Please allow notifications for this site in your browser settings.",
);
}
const registration = await navigator.serviceWorker.ready;
const vapidPublicKey = buildInfoQuery.data?.webpush_public_key;