fix(push): reconnect subscriptions after VAPID rotation

This commit is contained in:
SimpleTest 2026-07-24 20:44:02 +03:00
parent 39b7d0c6a3
commit 04eb0fe506

View File

@ -681,6 +681,34 @@ export const Hooks = {
return Uint8Array.from([...raw].map(character => character.charCodeAt(0)))
}
this.subscriptionUsesPublicKey = (subscription, publicKey) => {
const configuredKey = this.decodeApplicationServerKey(publicKey)
const subscribedKey = subscription?.options?.applicationServerKey
if (!subscribedKey) return false
const subscribedBytes = new Uint8Array(subscribedKey)
return subscribedBytes.length === configuredKey.length &&
subscribedBytes.every((byte, index) => byte === configuredKey[index])
}
this.subscriptionForPublicKey = async (registration, publicKey) => {
let subscription = await registration.pushManager.getSubscription()
if (subscription && !this.subscriptionUsesPublicKey(subscription, publicKey)) {
const unsubscribed = await subscription.unsubscribe()
if (!unsubscribed) throw new Error("stale_push_subscription_could_not_be_removed")
window.localStorage.removeItem("wnh.push.server-device-id")
subscription = null
}
return subscription || registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: this.decodeApplicationServerKey(publicKey)
})
}
this.installationId = () => {
const key = "wnh.push.installation-id"
const existing = window.localStorage.getItem(key)
@ -806,11 +834,7 @@ export const Hooks = {
}
const registration = await navigator.serviceWorker.ready
const subscription = await registration.pushManager.getSubscription() ||
await registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: this.decodeApplicationServerKey(publicKey)
})
const subscription = await this.subscriptionForPublicKey(registration, publicKey)
const json = subscription.toJSON()
await this.registerDevice({
@ -850,14 +874,20 @@ export const Hooks = {
try {
const registration = await navigator.serviceWorker.ready
const subscription = await registration.pushManager.getSubscription()
const currentKey = subscription &&
this.subscriptionUsesPublicKey(subscription, publicKey)
const deviceId = window.localStorage.getItem("wnh.push.server-device-id")
const activeServerDevice = deviceId &&
[...this.el.querySelectorAll("[data-disable-device]")]
.some(element => element.dataset.disableDevice === deviceId)
if (subscription && activeServerDevice) {
if (subscription && currentKey && activeServerDevice) {
this.setButtonState(true)
this.setStatus("Push notifications are enabled on this device.")
} else if (subscription && !currentKey) {
this.setStatus(
"Push keys changed. Enable notifications to reconnect this device securely."
)
} else if (subscription || activeServerDevice) {
this.setStatus("This browser has a push subscription. Enable it to reconnect this device.")
}