Browsers Web Push notifications¶
Supporting browsers¶
Desktop
- Chrome >= 50.0
- FireFox >= 44.0
- Safari >= 7
Mobile
- Google Chrome (Android only)
WebPush is working only with HTTPs.
Chrome/FireFox examples¶
Register your app on console.developers.google.com, and enable Cloud Messaging service.
After registration you must be have API KEY
and APP ID
.
- Add
GCM_KEY
withAPI KEY
to the project settings:
GCM_KEY = 'AIza...'
- Create
/static/manifest.json
with the following lines:
{
"gcm_sender_id": "``{APP ID}``",
"name": "Web Push Notification",
"short_name": "WebPush",
"icons": [
{
"src": "/static/images/icon-192x192.png",
"sizes": "192x192"
}
],
"start_url": "/?homescreen=1",
"display": "standalone",
"gcm_user_visible_only": true,
"permissions": [
"gcm"
]
}
- Add
/static/manifest.json
to meta on page
<head>
<meta charset="UTF-8">
...
<link rel="manifest" href="/static/manifest.json">
</head>
- Create service worker
/static/js/service-worker.js
file
self.addEventListener('push', function (event) {
if (event.data) {
var payload = event.data.json();
return self.registration.showNotification(payload.title, {
body: data.body,
icon: '/static/images/icon-192x192.png',
data: payload,
});
}
});
self.addEventListener('notificationclick', function (event) {
event.notification.close();
if (event.notification.data && event.notification.data.url) {
event.waitUntil(clients.matchAll({
type: "window"
}).then(function () {
if (clients.openWindow) {
return clients.openWindow(event.notification.data.url);
}
}));
}
});
- Register service worker to get permission and start background process
<head>
...
<script>
function enableWebPush() {
var is_chrome = navigator.userAgent.toLowerCase().indexOf('chrome') > -1;
var is_ff = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
if ((is_chrome || is_ff) && 'serviceWorker' in navigator) {
navigator.serviceWorker.register('/service-worker.js').then(function () {
navigator.serviceWorker.ready.then(function (serviceWorkerRegistration) {
serviceWorkerRegistration.pushManager.getSubscription().then(function (subscription) {
if (!subscription) {
serviceWorkerRegistration.pushManager.subscribe({userVisibleOnly: true}).then(function (subscription_info) {
var xhr = new XMLHttpRequest();
xhr.open("POST", "/dbmail/web-push/subscribe/", true);
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.setRequestHeader("X-CSRFToken", "{{ request.META.CSRF_COOKIE }}");
xhr.send(JSON.stringify(subscription_info));
document.getElementById('subscription').innerHTML = JSON.stringify(subscription_info);
});
}
document.getElementById('subscription').innerHTML = JSON.stringify(subscription);
});
});
});
}
}
</script>
</head>
<body onload="enableWebPush()">
<div id="subscription"></div>
...
- Open page to setup notification
$ open '/Applications/Google Chrome.app' --args https://localhost:8000/web-push/
- Install
pywebpush
app
$ pip install 'pywebpush>=0.4.0'
- Add Server-side Endpoints to
urls.py
urlpatterns += patterns(
'', url(r'^dbmail/', include('dbmail.urls')),
)
- Add events receiver (subscribe/unsubscribe)
from dbmail import signals
def _web_push(**kwargs):
# you must have your own function to store device token into db
kwargs.pop('instance', None)
kwargs.pop('sender', None)
kwargs.pop('signal', None)
print(kwargs)
signals.push_subscribe.connect(_web_push)
signals.push_unsubscribe.connect(_web_push)
- And finally you can send notification from backend
from dbmail.providers.google.browser import send
subscription_info = {
'endpoint': 'https://android.googleapis.com/gcm/send/dVj-T5fXaEw:AP...',
'keys': {
'auth': 'X6Ek_...',
'p256dh': 'BBo..'
}
}
send(subscription_info, message="Hello, World!", event="Python", url="..")
Safari examples¶
- Register a Website Push ID (requires iOS developer license or Mac developer license)
- Download and import to KeyChain the push notification certificate
- Exporting Private Key .p12 from KeyChain
- Generate .pem certificate to send notification via APNs
$ openssl pkcs12 -in apns-cert.p12 -out apns-cert.pem -nodes -clcerts
- Check APNs connection
$ openssl s_client -connect gateway.push.apple.com:2195 -CAfile apns-cert.pem
- Create contents of the Push Package
PushPackage.raw/
icon.iconset
icon_128x128@2x.png
icon_128x128.png
icon_32x32@2x.png
icon_32x32.png
icon_16x16@2x.png
icon_16x16.png
website.json
- Contents of
website.json
{
"websiteName": "Localhost",
"websitePushID": "web.ru.lpgenerator",
"allowedDomains": ["https://localhost:8000"],
"urlFormatString": "https://localhost:8000/%@",
"authenticationToken": "19f8d7a6e9fb8a7f6d9330dabe",
"webServiceURL": "https://localhost:8000/dbmail/safari"
}
- Create Push Package (https://github.com/connorlacombe/Safari-Push-Notifications)
$ cp `php createPushPackage.php` pushPackages.zip
- Add Server-side Endpoints to
urls.py
urlpatterns += patterns(
'', url(r'^dbmail/', include('dbmail.urls')),
)
- Add events receiver (subscribe/unsubscribe/errors)
from dbmail import signals
def _safari_web_push(**kwargs):
# you must have your own function to store device token into db
kwargs.pop('instance', None)
kwargs.pop('sender', None)
kwargs.pop('signal', None)
print(kwargs)
signals.safari_subscribe.connect(_safari_web_push)
signals.safari_unsubscribe.connect(_safari_web_push)
signals.safari_error_log.connect(_safari_web_push)
- Add
APNS_GW_HOST
andAPNS_CERT_FILE
to the project settings
APNS_GW_HOST = 'api.push.apple.com'
APNS_GW_PORT = 443
APNS_CERT_FILE = 'apns-cert.pem'
APNS_KEY_FILE = None
- Register service worker to get permission and start background process
<head>
...
<script>
function enableSafariWebPush() {
var websitePushID = "web.dev.localhost";
var webServiceUrl = "https://localhost:8000/web-push/";
var dataToIdentifyUser = {UserId: "123123"};
var checkRemotePermission = function (permissionData) {
if (permissionData.permission === 'default') {
window.safari.pushNotification.requestPermission(
webServiceUrl,
websitePushID,
dataToIdentifyUser,
checkRemotePermission
);
}
else if (permissionData.permission === 'denied') {
console.dir(arguments);
alert("Access denied. Please, enable push notification from Safari settings.");
}
else if (permissionData.permission === 'granted') {
document.getElementById('subscription').innerHTML = JSON.stringify(permissionData.deviceToken);
}
};
if ('safari' in window && 'pushNotification' in window.safari) {
checkRemotePermission(
window.safari.pushNotification.permission(websitePushID)
);
}
}
</script>
</head>
<body onload="enableSafariWebPush()">
<div id="subscription"></div>
...
- Open page to setup notification
$ open '/Applications/Safari.app' --args https://localhost:8000/web-push/
- And finally you can send notification from backend
from dbmail import send_db_push
send_db_push(
'welcome',
'62B63D730C84E363627B95879CF13723B890249A4BA03BAC08004574DF17D2DA',
use_celery=False,
alert={
"title": "Python",
"body": "Hello, World!",
"action": "View"
}, **{"url-args": ["https://localhost:8000/admin/"]}
)
Local demo¶
You can test by demo which found on repo or use samples
- Run server
$ cd demo
$ python manage.py runsslserver
- Copy path to SSL certs
export CERT_PATH=`python -c 'import os, sslserver; print(os.path.dirname(sslserver.__file__) + "/certs/development.crt")'`
echo $(CERT_PATH)
- Import certs to KeyChain
Linux¶
sudo apt-get install -y ca-certificates
sudo cp "$CERT_PATH" /usr/local/share/ca-certificates/
sudo update-ca-certificates
OS X¶
sudo security add-trusted-cert -d -r trustRoot -k "/Library/Keychains/System.keychain" "$CERT_PATH"
Windows (possible will not working)¶
certutil -addstore "Root" "$CERT_PATH"
# or
certmgr.exe -add -all -c "$CERT_PATH" -s -r localMachine Root
- Open demo url
$ open /Applications/Safari.app --args https://localhost:8000/web-push/
# or
$ open '/Applications/Google Chrome.app' --args https://localhost:8000/web-push/