Web-Push notification script working on Firefox but not on Chrome - javascript

I am building a web push notification system and I am using the concept used in this example:
https://github.com/Minishlink/web-push-php-example
I have the following code in my JS file. It checks for API support, checks if notifications are not disabled, registers the service worker, asks for permission to display notifications, if allowed subscribes the user and sends the details to the server. If the user is already subscribed, it updates the endpoint value in the DB.
When I run this on Firefox 61, it works fine, but when I run it on Chrome 67 I get this error:
Uncaught (in promise) TypeError: Cannot read property 'getKey' of null
at pushSubscribe (notification.js:48)
at navigator.serviceWorker.ready.then.then.subscription (notification.js:30)
My understanding is that Chrome does not detect the subscription when the service worker is registered and the user is subscribed, hence it gives the error. Any ideas how to fix this?
Thanks.
document.addEventListener('DOMContentLoaded', () => {
// Feature detection
if (!('serviceWorker' in navigator)) {
alert('Service Worker API isn’t supported.');
} else if (!('PushManager' in window)) {
alert('Push API isn’t supported.');
} else if (!('Notification' in window)) {
alert('Notifications API isn’t supported.');
} else if (!('showNotification' in ServiceWorkerRegistration.prototype)) {
alert('Notifications aren’t supported.');
// Check permission
} else if (Notification.permission == 'denied') {
alert('Notifications are disabled.');
} else {
// Register service worker
navigator.serviceWorker.register('/service-worker.js').then(() => {
navigator.serviceWorker.ready.then(serviceWorkerRegistration => serviceWorkerRegistration.pushManager.getSubscription()).then(subscription => {
if (!subscription) {
// Subscribe user
navigator.serviceWorker.ready.then(serviceWorkerRegistration => serviceWorkerRegistration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array('BNwjaNBKGM13IAef-gJr7C95W3yLJe2F5X0zLnwacN3zCnZK15Vqf3ijeHl9k7K0yBFX3ZwxAmldPoVDpi6iETA'),
})).then(subscription => {
return pushSubscribe(subscription);
});
}
// Update endpoint
return pushSubscribe(subscription);
});
});
function urlBase64ToUint8Array(base64String) {
const padding = '='.repeat((4 - base64String.length % 4) % 4);
const base64 = (base64String + padding).replace(/\-/g, '+').replace(/_/g, '/');
const rawData = window.atob(base64);
const outputArray = new Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
}
function pushSubscribe(subscription) {
const key = subscription.getKey('p256dh');
const token = subscription.getKey('auth');
const contentEncoding = (PushManager.supportedContentEncodings || ['aesgcm'])[0];
return fetch('/scripts/notification-subscribe.php', {
method: 'POST',
body: JSON.stringify({
endpoint: subscription.endpoint,
publicKey: key ? btoa(String.fromCharCode.apply(null, new Uint8Array(key))) : null,
authToken: token ? btoa(String.fromCharCode.apply(null, new Uint8Array(token))) : null,
contentEncoding,
user: userId, // generated beforehand
}),
}).then(() => subscription);
}
}
});

if(subscription){
const key = subscription.getKey('p256dh');
const token = subscription.getKey('auth');
const contentEncoding = (PushManager.supportedContentEncodings || ['aesgcm'])[0];
return fetch('/scripts/notification-subscribe.php', {
method: 'POST',
body: JSON.stringify({
endpoint: subscription.endpoint,
publicKey: key ? btoa(String.fromCharCode.apply(null, new Uint8Array(key))) : null,
authToken: token ? btoa(String.fromCharCode.apply(null, new Uint8Array(token))) : null,
contentEncoding,
user: 1,
}),
}).then(() => subscription);
}
}
Just modify this function hope pushSubscription with if block it works

Related

How to improve sequential promises execution and force fulfillment

This code is being used in a Sveltekit web application.
In the first step I get a user jwt token from an api like : dashboard.example.com/auth/local
and in the second step I'm using the response of the first api call to get full information from an api endpoint like this : example.com/api/users/token
This is an endpoint in an Sveltekit application:
import { json as json$1, error } from '#sveltejs/kit';
import axios from 'axios';
import md5 from 'md5';
import { SITE_ADDRESS } from '$lib/Env';
let userToken;
/** #type {import('#sveltejs/kit').RequestHandler} */
export async function POST({ request }) {
const bodyData = await request.json();
let identifier = bodyData.data.identifier;
let password = bodyData.data.password;
let loginToken = bodyData.data.loginToken;
let newLoginToken = md5(identifier + password + process.env.SECURE_HASH_TOKEN);
let dataResult = await axios
.post(`${import.meta.env.VITE_SITE_API}/auth/local`, {
identifier: identifier,
password: password
})
.then((response) => {
return response.data;
})
.then((response) => {
let userSummaryData = response;
userToken = md5(
userSummaryData.user.username + userSummaryData.user.id + process.env.SECURE_HASH_TOKEN
);
let userCompleteData = axios
.post(`${SITE_ADDRESS}/api/users/${userToken}`, {
data: {
userID: userSummaryData.user.id,
username: userSummaryData.user.username
}
})
.then((response) => {
return {
userJWT: userSummaryData.jwt,
userSummary: userSummaryData.user,
userFullSummary: response.data.userFullSummary
};
});
return userCompleteData;
})
.catch((error) => {
// console.log(' ---- Err ----');
});
if (dataResult && newLoginToken == loginToken) {
return json$1(
{
userJWT: dataResult.userJWT,
userSummary: dataResult.userSummary,
userFullSummary: dataResult.userFullSummary
},
{
headers: {
'cache-control': 'private, max-age=0, no-store'
}
}
);
} else if (dataResult && newLoginToken != loginToken) {
throw error(400, 'Something wrong happened');
}
throw error(401, 'Something wrong happened');
}
This code is work perfectly in localhost. But when I test it on host I get error 401.
and the question is :
Why this works on localhost but doesn't work on the server?
How can I improve this kind of promises (I'd like to use the response of the first api call in the second api call and return both
as a result)

navigator.serviceWorker.ready promise not getting resolved on push notifications on Production... Actually the code is working on development

I want to run the function configurePushSub down bellow whenever an event is fired on a page for my create-react-app... It is working everything fine on development mode, however when I hosted my app on Netlify the promise navigator.serviceWorker.ready is not getting resolved as I cannot see the message in the console navigator has loaded... I have already tried everything possible to get that working but everything unsuccessfull
function configurePushSub() {
if (!('serviceWorker' in navigator)) {
return;
}
let hasUnsubscribed;
var reg;
console.log(
'running',
navigator.serviceWorker,
navigator.serviceWorker.ready
);
navigator.serviceWorker.ready
.then(function (swreg) {
console.log(swreg, 'navigator has loaded');
reg = swreg;
return swreg.pushManager.getSubscription();
})
.then((sub) => {
if (!!sub) {
return sub.unsubscribe().then(function (s) {
hasUnsubscribed = true;
return navigator.serviceWorker.ready;
});
}
return navigator.serviceWorker.ready;
})
.then(function (sub) {
var vapidPublicKey =
'BOChVD1tKTc0Of3c-0JplT1y5FPOm6oijP_4stWBXwoQe6xI4GGt6cnpdu4JLwt_Znj23bj_hku8OSois1y9fLE';
var convertedVapidPublicKey = urlBase64ToUint8Array(vapidPublicKey);
return reg.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: convertedVapidPublicKey,
});
})
.then(async function (newSub) {
let key;
let authSecret;
let endPoint;
if (newSub) {
const rawKey = newSub.getKey ? newSub.getKey('p256dh') : '';
key = rawKey
? btoa(String.fromCharCode.apply(null, new Uint8Array(rawKey)))
: '';
var rawAuthSecret = newSub.getKey ? newSub.getKey('auth') : '';
authSecret = rawAuthSecret
? btoa(
String.fromCharCode.apply(null, new Uint8Array(rawAuthSecret))
)
: '';
endPoint = newSub.endpoint;
const config = {
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
};
const gpuTier = await getGPUTier();
const info = getOperatingSystemName(this);
console.log(info);
if (hasUnsubscribed) {
await axios.put(
`${Config.SERVER_ADDRESS}/api/pushNotifications/id`,
{
endpoint: endPoint,
key,
auth: authSecret,
gpu: gpuTier.gpu,
operating_system: `${info.os} ${info.osVersion}`,
device: info.mobile ? 'mobile' : 'desktop',
browser: `${info.browser} ${info.browserMajorVersion}`,
operating_system_architecture: info.arch,
},
config
);
} else {
await axios.post(
`${Config.SERVER_ADDRESS}/api/pushNotifications/`,
{
endpoint: endPoint,
key,
auth: authSecret,
gpu: gpuTier.gpu,
operating_system: `${info.os} ${info.osVersion}`,
device: info.mobile ? 'mobile' : 'desktop',
browser: `${info.browser} ${info.browserMajorVersion}`,
operating_system_architecture: info.arch,
},
config
);
}
}
})
.then(function (res) {
displayConfirmNotification();
})
.catch(function (err) {
console.log(err);
});
}
I have just managed to get that working...
It is just that I haven't registered the service worker for the production mode...
When I was on development I have written the following code, but then I moved to the production and I have forgotten about it.
navigator.serviceWorker.register(`/sw.js`)

Reactive Native TypeError: undefined is not an object (evaluating 'response.success')

I am trying to get an app working that was built using React Native. I get the error as per the screen shot. Can somebody please help me figure out what it means and how it can be fixed?
enter image description here
See below the code that is related to this error
tatic async func(route, params, verb) {
// console.warn(JSON.stringify(params));
let { orderStore } = Store;
const url = `${host}/${route}`
let options = Object.assign({ method: verb }, params ? { body: JSON.stringify(params) } : null);
options.headers = Api.headers()
const data = await LocalDb.getUserProfile();
if (params != null && params.type === 'social') {
const username = params.email;
const password = '1234';
const hash = new Buffer(`${username}:${password}`).toString('base64');
options.headers['Authorization'] = `Basic ${hash}`;
}
else
if (data != null) {
const username = data.email;
const password = data.password;
const hash = new Buffer(`${username}:${password}`).toString('base64');
options.headers['Authorization'] = `Basic ${hash}`;
}
return fetch(url, options).then(resp => {
// console.log('Api response is ------------->>>>>>', resp);
let json = resp.json();
if (resp.ok) {
return json
}
return json.then(err => { throw err });
}).catch(json => {
console.log('error is ', json)
// console.log('Api response is ------------->>>>>>', json);
Api.showAlert();
return;
});
}
tatic showAlert() {
// Works on both iOS and Android
Alert.alert(
'Network Error!',
'Click Ok To Restart App.',
[
{ text: 'OK', onPress: () => RNRestart.Restart() },
],
{ cancelable: false },
);
}

coverting javascript to python

I have a yale smart alarm and come across the the below javascript that allows you to access the alarm to get the status and set it. I'm wanting to use this in my home assistant set to which uses python.
const fetch = require('node-fetch');
const setCookie = require('set-cookie-parser');
const urls = {
login: 'https://www.yalehomesystem.co.uk/homeportal/api/login/check_login',
getStatus: 'https://www.yalehomesystem.co.uk/homeportal/api/panel/get_panel_mode',
setStatus: 'https://www.yalehomesystem.co.uk/homeportal/api/panel/set_panel_mode?area=1&mode=',
};
function getSessionCookie(username, password) {
let sessionCookie = null;
return fetch(urls.login, {
method: 'POST',
body: `id=${encodeURIComponent(username)}&password=${password}&rememberme=on&notify_id=&reg_id=Name`,
headers: {
'Accept': 'application/json, application/xml, text/plain, text/html, *.*',
'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'
},
})
.then((res) => {
sessionCookie = res.headers._headers['set-cookie'];
return res.json();
}).then(json => {
if (json.result === '0') {
return Promise.reject('Incorrect account details');
}
else {
return sessionCookie[0];
}
})
}
function getStatus(sessionCookie) {
return fetch(urls.getStatus, {
method: 'POST',
headers: {
'Cookie': sessionCookie,
},
}).then(res => res.text()).then(textResponse => {
// When initially writing this code I found if cookie payload
// was invalid I got this text response so I added this code to
// handle this, shouldn't happen but good to have an error message
// for this use case
if (textResponse === 'Disallowed Key Characters.') {
return Promise.reject('Invalid request');
}
else {
try {
// Hopefully if we got to this point we can parse the json
const json = JSON.parse(textResponse);
if (json.result === '0') {
return Promise.reject('Unable to get status');
}
else {
return json;
}
} catch (error) {
// If you get this error message I likely have not handled
// a error state that I wasnt aware of
return Promise.reject('Unable to parse response');
}
}
});
}
function setStatus (sessionCookie, mode) {
return new Promise((resolve, reject) => {
if (!sessionCookie || sessionCookie.length === 0) {
reject('Please call getSessionCookie to get your session cookie first');
}
if (mode !== 'arm' && mode !== 'home' && mode !== 'disarm') {
reject('Invalid mode passed to setStatus');
}
resolve(fetch(`${urls.setStatus}${mode}`, {
method: 'POST',
headers: {
'Cookie': sessionCookie,
},
}));
});
}
module.exports = {
getSessionCookie,
getStatus,
setStatus,
}
i'm every new to coding but was able to piece the below together to return the current status of my alarm. the problem is I'm unable to get it to work. based on the above code could someone please tell me what I'm missing, or if I'm going down the wrong rabbit hole....
import requests
import webbrowser
url = “https://www.yalehomesystem.co.uk/homeportal/api/login/check_login”
payload = {‘username’: ‘email#domaim.com’, ‘password’: ‘mypass’}
with requests.session() as s:
# fetch the login page
s.get(url, data=payload)
url1='https://www.yalehomesystem.co.uk/homeportal/api/panel/get_panel_mode'
# post to the login form
r = s.post(url1, data=payload)
print(r.text)
To add more contexts I'm getting the following error
{"result":"0","message":"system.permission_denied","code":"999"}

Web Pushnotification 'UnauthorizedRegistration' or 'Gone' or 'Unauthorized'- subscription expires

I have developed a push notification service for my web site. the service worker is:
'use strict';
self.addEventListener('push', function (event) {
var msg = {};
if (event.data) {
msg = event.data.json();
}
let notificationTitle = msg.title;
const notificationOptions = {
body: msg.body,//body
dir:'rtl',//direction
icon: msg.icon,//image
data: {
url: msg.url,//click
},
};
event.waitUntil(
Promise.all([
self.registration.showNotification(
notificationTitle, notificationOptions),
])
);
});
self.addEventListener('notificationclick', function (event) {
event.notification.close();
let clickResponsePromise = Promise.resolve();
if (event.notification.data && event.notification.data.url) {
clickResponsePromise = clients.openWindow(event.notification.data.url);
}
const fetchOptions =
{ method: 'post'};
fetch('http://localhost:5333/usrh.ashx?click=true', fetchOptions).
then(function (response)
{
if (response.status >= 400 && response.status < 500)
{
throw new Error('Failed to send push message via web push protocol');
}
}).catch((err) =>
{
this.showErrorMessage('Ooops Unable to Send a Click', err);
});
});
self.addEventListener('notificationclose', function (event) {
const fetchOptions =
{ method: 'post'};
fetch('http://localhost:5333/usrh.ashx?close=true', fetchOptions).
then(function (response)
{
if (response.status >= 400 && response.status < 500)
{
throw new Error('Failed to send push message via web push protocol');
}
}).catch((err) =>
{
this.showErrorMessage('Ooops Unable to Send a Click', err);
});
});
self.addEventListener('pushsubscriptionchange', function () {
const fetchOptions = {
method: 'post'
,
};
fetch('http://localhost:5333/usru.ashx', fetchOptions)
.then(function (response) {
if (response.status >= 400 && response.status < 500) {
console.log('Failed web push response: ', response, response.status);
throw new Error('Failed to update users.');
}
})
.catch((err) => {
this.showErrorMessage('Ooops Unable to Send a user', err);
});
});
I have subscribed the users successfully using the following code:
registerServiceWorker() {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('http://localhost:5333/service-worker.js')
.catch((err) => {
this.showErrorMessage('Unable to Register SW', 'Sorry this demo requires a service worker to work and it ' + 'failed to install - sorry :(');
console.error(err);
});
} else {
this.showErrorMessage('Service Worker Not Supported', 'Sorry this demo requires service worker support in your browser. ' +
'Please try this demo in Chrome or Firefox Nightly.');
}
}
and
class PushClient {
constructor(subscriptionUpdate, appkeys) {
this._subscriptionUpdate = subscriptionUpdate;
this._publicApplicationKey = appkeys;
if (!('serviceWorker' in navigator)) {
return;
}
if (!('PushManager' in window)) {
return;
}
if (!('showNotification' in ServiceWorkerRegistration.prototype)) {
return;
}
navigator.serviceWorker.ready.then(() => {
this.setUpPushPermission();
});
}
setUpPushPermission() {
return navigator.serviceWorker.ready.then((serviceWorkerRegistration) => {
return serviceWorkerRegistration.pushManager.getSubscription();
})
.then((subscription) => {
if (!subscription) {
return;
}
this._subscriptionUpdate(subscription);
})
.catch((err) => {
console.log('setUpPushPermission() ', err);
});
}
subscribeDevice() {
return new Promise((resolve, reject) => {
if (Notification.permission === 'denied') {
sc(3);
return reject(new Error('Push messages are blocked.'));
}
if (Notification.permission === 'granted') {
sc(3);
return resolve();
}
if (Notification.permission === 'default') {
Notification.requestPermission((result) => {
if (result === 'denied') {
sc(0);
} else if (result === 'granted') {
sc(1);
} else {
sc(2);
}
if (result !== 'granted') {
reject(new Error('Bad permission result'));
}
resolve();
});
}
})
.then(() => {
return navigator.serviceWorker.ready.then((serviceWorkerRegistration) => {
return serviceWorkerRegistration.pushManager.subscribe({
userVisibleOnly: true
, applicationServerKey: this._publicApplicationKey.publicKey
,
});
})
.then((subscription) => {
this._subscriptionUpdate(subscription);
if (subscription) {
this.sendPushMessage(subscription);
}
})
.catch((subscriptionErr) => { });
})
.catch(() => { });
}
toBase64(arrayBuffer, start, end) {
start = start || 0;
end = end || arrayBuffer.byteLength;
const partialBuffer = new Uint8Array(arrayBuffer.slice(start, end));
return btoa(String.fromCharCode.apply(null, partialBuffer));
}
unsubscribeDevice() {
navigator.serviceWorker.ready.then((serviceWorkerRegistration) => {
return serviceWorkerRegistration.pushManager.getSubscription();
})
.then((pushSubscription) => {
if (!pushSubscription) {
this._subscriptionUpdate(null);
return;
}
return pushSubscription.unsubscribe()
.then(function (successful) {
if (!successful) {
console.error('We were unable to unregister from push');
}
});
})
.then(() => {
this._subscriptionUpdate(null);
})
.catch((err) => {
console.error('Error thrown while revoking push notifications. ' + 'Most likely because push was never registered', err);
});
}
sendPushMessage(subscription) {
let payloadPromise = Promise.resolve(null);
payloadPromise = JSON.parse(JSON.stringify(subscription));
const vapidPromise = EncryptionHelperFactory.createVapidAuthHeader(this._publicApplicationKey, subscription.endpoint, 'http://localhost:5333/');
return Promise.all([payloadPromise, vapidPromise, ])
.then((results) => {
const payload = results[0];
const vapidHeaders = results[1];
let infoFunction = this.getWebPushInfo;
infoFunction = () => {
return this.getWebPushInfo(subscription, payload, vapidHeaders);
};
const requestInfo = infoFunction();
this.sendRequestToProxyServer(requestInfo);
});
}
getWebPushInfo(subscription, payload, vapidHeaders) {
let body = null;
const headers = {};
headers.TTL = 60;
if (payload) {
headers.Encryption = `auth=${payload.keys.auth}`;
headers['Crypto-Key'] = `p256dh=${payload.keys.p256dh}`;
headers['Content-Encoding'] = 'aesgcm';
} else {
headers['Content-Length'] = 0;
}
if (vapidHeaders) {
headers.Authorization = `WebPush ${vapidHeaders.authorization}`;
if (headers['Crypto-Key']) {
headers['Crypto-Key'] = `${headers['Crypto-Key']}; ` + `p256ecdsa=${vapidHeaders.p256ecdsa}`;
} else {
headers['Crypto-Key'] = `p256ecdsa=${vapidHeaders.p256ecdsa}`;
}
}
const response = {
headers: headers
, endpoint: subscription.endpoint
,
};
if (body) {
response.body = body;
}
return response;
}
sendRequestToProxyServer(requestInfo) {
const fetchOptions = {
method: 'post'
,
};
if (requestInfo.body && requestInfo.body instanceof ArrayBuffer) {
requestInfo.body = this.toBase64(requestInfo.body);
fetchOptions.body = requestInfo;
}
fetchOptions.body = JSON.stringify(requestInfo);
fetch('http://localhost:5333/usrh.ashx', fetchOptions)
.then(function (response) {
if (response.status >= 400 && response.status < 500) {
console.log('Failed web push response: ', response, response.status);
throw new Error('Failed to send push message via web push protocol');
}
})
.catch((err) => {
this.showErrorMessage('Ooops Unable to Send a Push', err);
});
}
}
All these codes are in javascript. I can successfully recieve user subscription infromarion on my server like:
Authorization: WebPush eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJhdWQiOiJodHRwcxxxxx
Crypto-Key: p256dh=BBp90dwDWxxxxc1TfdBjFPqxxxxxwjO9fCip-K_Eebmg=; p256ecdsa=BDd3_hVL9fZi9Yboxxxxxxo
endpoint: https://fcm.googleapis.com/fcm/send/cxxxxxxxxxxxxxxJRorOMHKLQ3gtT7
Encryption: auth=9PzQZ1mut99qxxxxxxxxxxyw==
Content-Encoding: aesgcm
Also I can successfully send a push to this user using the code bellow in C#:
public static async Task<bool> SendNotificationByte(string endpoint, string[] Keys, byte[] userSecret, byte[] data = null,
int ttl = 0, ushort padding = 0, bool randomisePadding = false, string auth="")
{
#region send
HttpRequestMessage Request = new HttpRequestMessage(HttpMethod.Post, endpoint);
Request.Headers.TryAddWithoutValidation("Authorization", auth);
Request.Headers.Add("TTL", ttl.ToString());
if (data != null && Keys[1] != null && userSecret != null)
{
EncryptionResult Package = EncryptMessage(Decode(Keys[1]), userSecret, data, padding, randomisePadding);
Request.Content = new ByteArrayContent(Package.Payload);
Request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
Request.Content.Headers.ContentLength = Package.Payload.Length;
Request.Content.Headers.ContentEncoding.Add("aesgcm");
Request.Headers.Add("Crypto-Key", "dh=" + Encode(Package.PublicKey)+" ;"+Keys[2]+"="+Keys[3]);
Request.Headers.Add("Encryption", "salt=" + Encode(Package.Salt));
}
using (HttpClient HC = new HttpClient())
{
HttpResponseMessage res = await HC.SendAsync(Request).ConfigureAwait(false);
if (res.StatusCode == HttpStatusCode.Created)
return true;
else return false;
}
#endregion
}
The problem is that after a period of time (about 20 hours or even less), when I want to send a push to this user I got the following errors:
firefox subscription:
{StatusCode: 410, ReasonPhrase: 'Gone', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
Access-Control-Allow-Headers: content-encoding,encryption,crypto-key,ttl,encryption-key,content-type,authorization
Access-Control-Allow-Methods: POST
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: location,www-authenticate
Connection: keep-alive
Cache-Control: max-age=86400
Date: Tue, 21 Feb 2017 08:19:03 GMT
Server: nginx
Content-Length: 179
Content-Type: application/json
}}
chrome subscription:
{StatusCode: 400, ReasonPhrase: 'UnauthorizedRegistration', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
Alt-Svc: quic=":443"; ma=2592000; v="35,34"
Vary: Accept-Encoding
Transfer-Encoding: chunked
Accept-Ranges: none
Cache-Control: max-age=0, private
Date: Tue, 21 Feb 2017 08:18:35 GMT
Server: GSE
Content-Type: text/html; charset=UTF-8
Expires: Tue, 21 Feb 2017 08:18:35 GMT
}}
I think I missed something, that makes the subscription expires, or have to make the users to resubscribe when their subscription information is changed or expired, but I do not know how?!!
The Problem is solved by sending a push echo notification to the subscribed users to resubscribe them. I have wrote a job in which I send a push echo periodically and resubscribe the users and update their information.
To do so I send an special message called "push echo" using the code bellow:
self.addEventListener('push', function (event) {
lastEventName = 'push';
var msg = {};
if (event.data) {
msg = event.data.json();
if (!!msg.isEcho) {
self.registration.pushManager.getSubscription()
.then(function (subscription) {
if (!subscription) {
} else {
subscription.unsubscribe().then(function () {
self.registration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: base64UrlToUint8Array('xxxxxxxxxxxxxxxx') })
.then(function (subscription) {
resubscription(subscription);
});
});
}
});
return;
}
}
if (!!msg.isEcho)
return;
let notificationTitle = msg.title;
const notificationOptions = {
body: msg.body,
dir: 'rtl',
icon: msg.icon,
data: {
url: msg.url,
id: msg.id,
key: msg.key
},
};
event.waitUntil(
Promise.all([
self.registration.showNotification(
notificationTitle, notificationOptions),
])
);
const fetchOptions =
{ method: 'post', mode: 'no-cors' };
fetch('http://example.com', fetchOptions).
then(function (response) {
if (response.status >= 400 && response.status < 500) {
throw new Error('Failed to send push message via web push protocol');
}
lastEventName = 'view';
}).catch((err) => {
this.showErrorMessage('Ooops Unable to Send a Click', err);
});
});
In the resubscription method you can unsubscribe and then subscribe the user and update server data.
I think the issue is about how you send your applicationServerKey. I just have done an example of what you want to do and I had to send that key encoded with this function:
function urlBase64ToUint8Array(base64String) {
const padding = '='.repeat((4 - base64String.length % 4) % 4);
const base64 = (base64String + padding)
.replace(/\-/g, '+')
.replace(/_/g, '/');
const rawData = window.atob(base64);
const outputArray = new Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
}
so you have to create your subscription object in this way :
registration.pushManager
.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(this._publicApplicationKey.publicKey),
})
Mainly what I did was follow this tutorial.
I have that working example in this github repo. The README file is in Spanish, but I think it can help you.
I think the problem can be solved by resubscribing the users.
Some clues:
The sw needs to be registered and activated, at the time the push event arrives. This mean that you may not clean session, use private browsing mode, clean cache of computer in between.
The push event must come from the same origin.

Categories

Resources