带有 VAPID 的 Web 推送:400/401 未经授权的注册

Wik*_*iti 7 ruby ruby-on-rails http web-push vapid

首先,我已经检查了这些问题,但没有任何运气:

我正在尝试为我正在开发的网络应用程序实施网络推送通知。目前,我已经实现了以下目标:

  • 创建一个 VAPID 密钥对(使用本指南)。
  • 注册一个 Service Worker(只需要一个 Service Worker,我认为manifest.json不再需要)。
  • 将用户订阅到服务器(订阅数据将存储在数据库中)。
  • 使用webpush gem发送推送通知。

设置后,一切正常(在本地主机和远程机器上)。但是,几个小时后(12 到 24 小时之间),通知在远程机器上停止工作(本地主机工作正常)。在那之后,从服务器端(Rails)发送推送通知时会抛出以下错误:

  • 铬:( UnauthorizedRegistration 400没有额外的信息)。
  • 火狐: {"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"}

出现此错误后,我尝试在每次访问页面时取消订阅并重新订阅用户。重新订阅完成后更新了db上的订阅字段,但仍然抛出错误,信息相同。浏览器和 Service Worker 都不会抛出错误。

我试图通过在 Chrome Dev Tools 控制台上放置一些 js 代码、取消注册服务工作者并重置推送通知权限来手动强制重新订阅,但没有解决错误。我只能通过创建一个新的 VAPID 密钥对来重新启动远程机器来修复这个错误。重新启动机器后,我还有 12-24 小时它再次失败。此外,通知发送过程在rails server (nginx + unicorn)rails consoleirb上都不起作用。

我不知道从哪里开始。更糟糕的是,我每天只能尝试修复一次,因为它每 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()
Run Code Online (Sandbox Code Playgroud)

用户订阅和重新订阅

# 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
Run Code Online (Sandbox Code Playgroud)

服务人员:

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

  notificationPromise = self.registration.showNotification(title, options)
  event.waitUntil(notificationPromise)
Run Code Online (Sandbox Code Playgroud)

网页推送调用:

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

Wik*_*iti 1

将过期时间从 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
  }
)
Run Code Online (Sandbox Code Playgroud)