Web Push с VAPID: 400/401 Неавторизованная регистрация

Прежде всего, я уже безуспешно проверял эти вопросы:

Я пытаюсь реализовать веб-push-уведомления для веб-приложения, над которым я работаю. На данный момент я достиг следующих целей:

После настройки все работает нормально (на локальном хосте и на удаленной машине). Однако через несколько часов (от 12 до 24 часов) уведомления перестают работать на удаленной машине (localhost работает отлично). По истечении этого времени при отправке push-уведомлений со стороны сервера (Rails) возникают следующие ошибки:

  • Chrome: UnauthorizedRegistration 400 (без дополнительной информации).
  • Firefox: {"code": 401, "errno": 109, "error": "Unauthorized", "more_info": "http://autopush.readthedocs.io/en/latest/http.html#error-codes", "message": "Request did not validate Invalid bearer token: Auth > 24 hours in the future"}

После появления этой ошибки я пытался отписаться и переподписать пользователя при каждом посещении страницы. Поле подписки в базе данных обновляется после повторной подписки, но ошибка все еще возникает с той же информацией. Никаких ошибок ни в браузере, ни в сервис-воркере не выдается.

Я попытался принудительно выполнить повторную подписку вручную, поместив некоторый код js в консоль Chrome Dev Tools, отменив регистрацию работника службы и сбросив разрешения на push-уведомления, но ничто не устраняет ошибку. Я могу исправить эту ошибку, только создав новую пару ключей VAPID, перезагрузив удаленную машину. После создания машины у меня есть еще 12-24 часа, прежде чем она снова выйдет из строя. Кроме того, процесс отправки уведомлений не работает ни на сервере rails (nginx + unicorn), ни на консоли rails, ни на irb.

Я не знаю, куда идти теперь. Хуже того, я могу исправлять только один раз в день, так как он ломается каждые 24 часа. Думаю, мне нужна внешняя помощь со свежим видением проблемы. Я что-то упускаю? Есть ли какая-либо зависимость от ОС, которую использует VAPID и которую необходимо перезапустить?


Вот несколько фрагментов кода, которые могут быть полезны. Извините за беспорядок, но я сделал массу модификаций, чтобы попытаться заставить его работать.

Регистрация сервисного работника:

serviceWorkerRegistration = null

registerServiceWorker = ->
  if 'serviceWorker' of navigator && 'PushManager' of window
    navigator.serviceWorker.register("<js url>").then (reg) ->
      serviceWorkerRegistration = reg
      checkSubscription()

    .catch (error) ->
      console.warn 'Service Worker Error', error

  else
    console.warn 'Push messaging is not supported'

registerServiceWorker()

Подписка пользователя и повторная подписка

# Refresh user subscription (unsub + sub)
refreshUserSubscription = ->
  unsubscribeUser(subscribeUser)

# Subscribe the user to the push server
subscribeUser = ->
  return if !serviceWorkerRegistration

  # Subscribe
  serviceWorkerRegistration.pushManager.subscribe
    userVisibleOnly: true
    applicationServerKey: urlB64ToUint8Array('...')

  .then (subscription) ->
    pushSubscription subscription

  .catch (err) ->
    console.warn 'Failed to subscribe the user: ', err

# Unsubscribe user
unsubscribeUser = (callback)->
  return unless serviceWorkerRegistration

  serviceWorkerRegistration.pushManager.getSubscription().then (subscription) ->
    if subscription
      subscription.unsubscribe().then(callback)
    else
      callback()
  .catch (error) ->
    console.warn 'Error unsubscribing', error

# Push subscription to the web app
pushSubscription = (subscription) ->
  data = if subscription then subscription.toJSON() else {}
  $.post "<back-end endpoint>", subscription: data

# Fetch current subscription, and push it.
checkSubscription = () ->
  serviceWorkerRegistration.pushManager.getSubscription()
    .then (subscription) ->
      pushSubscription(subscription)

# I think that this should be done with promises instead of a simple timeout.
setTimeout refreshUserSubscription, 1000

Работник службы:

self.addEventListener 'push', (event) ->
  title = "Example"
  options = { body: "Example" }

  notificationPromise = self.registration.showNotification(title, options)
  event.waitUntil(notificationPromise)

Веб-вызов:

webpush = WebPush.new({ endpoint: '...', keys: { p256dh: '...', auth: '...' } })
webpush.set_vapid_details(
  "mailto:#{CONTACT_EMAIL}",
  "<base64 public key>",
  "<base64 private key>"
)
webpush.send_notification("foo")

person Wikiti    schedule 26.04.2017    source источник


Ответы (2)


После изменения срока действия с 24 часов (по умолчанию) на 12 часов, теперь он работает правильно:

Webpush.payload_send(
  message: "Hi!",
  endpoint: "...",
  p256dh: "...",
  auth: "...",
  vapid: {
    subject: "...",
    public_key: ENV['VAPID_PUBLIC_KEY'],
    private_key: ENV['VAPID_PRIVATE_KEY'],
    exp: 12.hours
  }
)
person Wikiti    schedule 04.05.2017
comment
привет, я пытаюсь реализовать нечто подобное (stackoverflow.com/questions/50361320/). один момент, который я не могу понять, это если мне нужно обновить регистрацию подписки браузера, или когда браузер разрешает уведомления и отправляет подписку, это все, что нужно для отправки уведомлений на неопределенный срок? Я получаю 401 NotRegistered через 24 часа или около того. - person CLod; 16.05.2018

Похоже, библиотека изменилась, вот как я это исправил:

def send_push(payload, endpoint, p256dh, auth)
  Webpush.payload_send(
    message: payload,
    endpoint: endpoint,
    p256dh: p256dh,
    auth: auth,
    vapid: {
      public_key: '...',
      private_key: '...',
      expiration: 12 * 60 * 60
    }
  )
end

Обратите внимание, что срок действия ключа не истекает, и это время истечения срока действия в секундах.

person David Parish    schedule 08.11.2017