Web Push with VAPID: 400/401 Unauthorized Registration
First of all, I have already checked these questions with no luck:
- Chrome Web Push API Returning "Unauthorized Registration"
- [WebPush] [VAPID] Request does not work with 400 UnauthorizedRegistration
I am trying to implement web push notifications for a web application that I am working on. Currently I have achieved the following goals:
- Create a VAPID key pair ( with this tutorial ).
- Register a service worker (only a service worker, I guess
manifest.json
not needed anymore). - Subscribe the user to the server (the subscription data will be saved in the database).
- Send push notification with webpush gem.
After setup everything works fine (on localhost and on remote machine). However, after a few hours (12 to 24 hours) the notifications stop working on the remote machine (localhost works fine). After this time, when sending push notifications from the server side (Rails), the following errors occur:
- Chrome:
UnauthorizedRegistration 400
(no more info). - 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"}
After getting this error, I tried to unsubscribe and redirect the user on every visit to the page. The db subscription field is updated after the re-subscription is complete, but the error is still being thrown with the same information. Errors do not occur in the browser and are not serviced by the service.
I tried to force the re-subscription by putting some js code in the Chrome Dev Tools console, unregistering the service worker and restarting the push notification permissions, but nothing solves the problem. I can only fix this error by creating a new VAPID key pair by restarting the remote computer . After painting the car, I still have 12-24 hours before it fails again. Also, the process of sending notifications does not work on either rails server (nginx + unicorn), rails console, or irb.
I don't know where to start. Even in the worst case, I can only fix the fix once a day, as it will break every 24 hours. I think I need outside help with a new vision of the problem. Am I missing something? Is there any OS dependency that VAPID is using and needs to be restarted?
Here are some code snippets you might find useful. Sorry for the mess, but I did a few modifications to try and get it to work.
Service worker registration:
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()
User subscription and re-subscription
# 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
Service worker:
self.addEventListener 'push', (event) ->
title = "Example"
options = { body: "Example" }
notificationPromise = self.registration.showNotification(title, options)
event.waitUntil(notificationPromise)
Calling back over the Internet:
webpush = WebPush.new({ endpoint: '...', keys: { p256dh: '...', auth: '...' } })
webpush.set_vapid_details(
"mailto:#{CONTACT_EMAIL}",
"<base64 public key>",
"<base64 private key>"
)
webpush.send_notification("foo")
source to share
After changing the expiration date from 24 hours (default) to 12 hours, it now works correctly:
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
}
)
source to share
It looks like the library has changed, here's how I fixed it:
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
Note that the key expires, not exp, and the time expires in seconds.
source to share