diff --git a/web/src/utils/firebasePush.ts b/web/src/utils/firebasePush.ts index 737d029..230bcd1 100644 --- a/web/src/utils/firebasePush.ts +++ b/web/src/utils/firebasePush.ts @@ -9,7 +9,7 @@ let foregroundListenerAttached = false; export async function ensureWebPushRegistration(): Promise { const config = getFirebaseConfig(); - const vapidKey = import.meta.env.VITE_FIREBASE_VAPID_KEY?.trim(); + const vapidKey = normalizeVapidKey(import.meta.env.VITE_FIREBASE_VAPID_KEY); if (!config || !vapidKey) { return; } @@ -33,10 +33,21 @@ export async function ensureWebPushRegistration(): Promise { const registration = await navigator.serviceWorker.ready; const app = getApps()[0] ?? initializeApp(config); const messaging = getMessaging(app); - const token = await getToken(messaging, { - vapidKey, - serviceWorkerRegistration: registration, - }); + let token: string | null = null; + try { + token = await getToken(messaging, { + vapidKey, + serviceWorkerRegistration: registration, + }); + } catch (error) { + if (error instanceof DOMException && error.name === "InvalidAccessError") { + console.error( + "[web-push] Invalid VAPID key format. Check VITE_FIREBASE_VAPID_KEY in web env.", + ); + return; + } + throw error; + } if (!token) { return; } @@ -107,3 +118,34 @@ function getFirebaseConfig(): appId, }; } + +function normalizeVapidKey(raw: string | undefined): string | null { + if (!raw) { + return null; + } + let key = raw.trim(); + if (!key) { + return null; + } + + // Accept accidental JSON payloads copied from docs/panels. + if (key.startsWith("{") && key.endsWith("}")) { + try { + const parsed = JSON.parse(key) as { vapidKey?: string; publicKey?: string; key?: string }; + key = (parsed.vapidKey ?? parsed.publicKey ?? parsed.key ?? "").trim(); + } catch { + return null; + } + } + + // Strip wrapping quotes and whitespace/newlines. + key = key.replace(/^['"]|['"]$/g, "").replace(/\s+/g, ""); + + // Convert classic base64 chars to URL-safe format expected by PushManager/Firebase. + key = key.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, ""); + + if (!/^[A-Za-z0-9\-_]+$/.test(key)) { + return null; + } + return key.length >= 80 ? key : null; +}