At OneSignal, we'd like to allow AMP page visitors to subscribe to web push notifications. Here's a demo of web push notifications (regular page, not an AMP page). This AMP extension integrates with our notifications platform service.
Is the visitor subscribed? Create a hidden iFrame to the site's canonical origin to query the visitor's subscription state
A user may already be subscribed to push. If subscribed, we don't show subscription widgets. The user's subscription state is stored in the canonical origin's site storage. Because AMP pages can be served on a non-site origin like Google's AMP cache, we create an iFrame to the canonical origin to access this site storage, and use postMessage() to get the user's subscription state.
The iFrame is hidden and to a lightweight page just for querying subscription state.
Show a subscription widget for unsubscribed visitors
Developers write an AMP HTML button or link that says "Subscribe to push notifications".
This subscription widget can be anywhere in the AMP page, guarded by custom visibility attributes which our extension will parse and style the element visibly or invisibly depending on the user's subscription state.
A visitor clicking "Subscribe" is subscribed through a popup window to the canonical origin that loads our JavaScript SDK
Widget disappears, and subscriber resumes browsing
The next time the subscriber visits this page, the subscription widget will be hidden.
Code so far is available in our forked version. [Not completed! In the beginning stages.]
Developers set the iFrame URL through a <script type='application/json'>. This must match the AMP page's canonical origin to prevent linking to an unrelated site.
<!-- OneSignal AMP Extension Configuration
ampHelperUrl: Provide an absolute URL to the subscription helper page. (e.g. 'https://my-site.com/onesignal-amp-check'). This must match your AMP Page's canonical origin.
-->
<script id="amp-onesignal-config" type="application/json">
{
"ampHelperUrl": "https://my-site.com/onesignal-amp-check"
}
</script>
We load https://my-site.com/onesignal-amp-check in a hidden and sandboxed allow-same-origin allow-scripts iFrame and postmessage to check their subscription status.
Their AMP HTML, with our extension-specific attributes to hide or show their custom subscription widgets based on their subscription status:
```
Some article text.
Whether the user denies or grants permissions, the popup will close, leaving the user on the original AMP page.
What would it take to generalise this to work for other services that provide push notifications on the web?
Why does this need to be a custom component? It sounds like all use cases are already covered by amp-access.
Hey @sebastianbenz,
I think amp-access checks for a JSON { success: true } response from an authorization URL a developer defines, but we need to access client-sided storage data, so the authorization URL the developer defines needs to be a page that runs some JS to check the storage and then postMessage() the result back. A server-only response wouldn't allow JavaScript to access the site's storage state.
Hey @adewale,
We'll brainstorm how to generalize this to fit other push vendors and web notifications more generally.
@jasonpang you can check notification state via cookies in the auth endpoint. The auth request can return arbitrary JSON, e.g. { "notification": "permission_denied" }. You can set the cookie in the subscribe popup, which you can trigger via amp-access login action.
Hey @sebastianbenz,
As you mentioned, amp-access checks subscription state by making a request to the origin server, and we could have the server detect a "user is subscribed" cookie passed along with the request.
But tracking subscription state using cookies isn't as accurate and can't reflect the user's actual subscription state.
PushManager.getSubscription() return value (null if unsubscribed, otherwise it will have an endpoint). More accurate tracking can give site devs an opportunity to provide a better user experience.Also, amp-access's request —> JSON response strategy has some disadvantages for a push subscription-only model.
Hey @adewale,
We'd be happy to generalize this component as a web push notifications AMP extension (<amp-web-push>) for all vendors. Based on the existing proposal, this can be easily done with vendors customizing the iFrame and popup HTML page to perform vendor-specific actions. For example:
Checking a visitor's subscription
The iFrame replies true or false.
The iFrame page we provide can be tweaked per-vendor to implement isSubscribed() differently. For example, for our service, we'd additionally check an IndexedDb flag that describes whether a user prefers to temporarily mute notifications. Other vendors may check other preferences.
Determining whether to show the subscription widget
The iFrame replies true or false.
Vendors have the flexibility to respond to a variety of user states to provide the best user experience. For example, we can detect:
Subscribing the visitor
A popup window opens to a new page to subscribe the user.
Vendors can provide a tweaked popup page to register their uniquely named service worker.
For the best user experience, minimal work is done in the popup and the popup is closed quickly. Parts of the subscription, like posting to the push vendor to store subscription details, can be done after in the iFrame.
If this proposal looks good, should we continue working on getting out a small proof of concept?
Hey @adewale @sebastianbenz,
We've been hard at work on a proof of concept to demo an early version of our AMP extension and how it would work. We're happy to show a demo of an alpha version of our plugin!
AMP Push Notifications Extension Demo
You should see a blue _Subscribe to Notifications_ button.

We open a hidden, lightweight, sandboxed iFrame to the canonical origin

The frames accepts queries from the host page: e.g., should the visitor see the subscription widget?

Each push vendor provides their own implementation of isSubscribedToPushNotifications(). Sites modify the above code to control the visibility of subscription widgets.
This button _subscription widget_ is defined by the AMP site

Sites can add multiple named subscription widgets
Here's a _Thanks for subscribing!_ widget, shown only after subscribing

This way, users can customize the amount of widgets to display, the location of each widget, and the visibility of each widget.
Click _Subscribe to Notifications_. A popup window will open.

Both the popup and frame URLs are preset in an AMP configuration section, similar to amp-access

The popup, like the iframe, communicates via postMessage() to indicate when the subscription is complete or canceled
Each push vendor provides their own implementation of the subscription popup
You can click X to cancel the permission request. You can also block it (learn how to grant permissions again here). Eventually, Allow the notification permission.
You're now subscribed!

Subscribe Popup Source Code: view-source: https://onesignal-staging.pw/webpush/amp/subscribe?https=1
Promise-based MessageChannel/PostMessage Helper
We look forward to any feedback you have regarding our implementation!
Here is the amp-web-push extension source code.
Aside from adding tests, adding types via comments, cleaning up the code a bit, this code is how we envision the first release of the plugin to be.
Note: Although the first link you're opening looks like the same domain as the popup that opens, the first page is supposed to represent an AMP page on something like Google's cache. It should work even if the domain is completely different and it's a full-on AMP page.
Sorry for the late response 😞. Somehow this slipped through my filter and then I've been on vacation for a while.
Adding @cramforce @dvoytenko @ericlindley-g for feedback.
Two things that come to my mind:
Would it be possible to embed your notification widget via amp-iframe?
A few thoughts:
/cc @aghassemi for visibility as well
@jasonpang — do you have a new link to the demo? Would like to check out the UI if it's easy to get running again.
@sebastianbenz @cramforce @ericlindley-g thanks for your replies!
We've made another demo available at this link (with a GIF recording just in case):
@sebastianbenz:
What are best practices on avoiding layout reflows using static widget dimensions?
To prevent reflow, should we display an empty placeholder space while the widget calculates whether it should display itself? If no widget is eventually displayed (e.g. user already subscribed), the empty space would persist on the page (removing the empty space would cause layout jumps) and look irregular.
But without an empty placeholder space, there would be layout jumps as our widget inserts itself on the page.
Running arbitrary code in a hidden iframe seems unavoidable
It doesn't seem possible to prevent this, and our approach shouldn't be any less secure than using amp-iframe.
amp-iframe also makes it possible to run arbitrary code, and as long as an iFrame is used, users can run custom scripts.
Some restrictions we can add: our extension can refuse to load a third-party URL (to be added) by requiring the origin to match the site's canonical origin. Despite our best efforts to check the URL, developers can still point to a custom script hosted on their origin, so this seems like the best we can do. Our hidden iFrame lives on the site's canonical origin, so scripts won't interfere with the actual AMP page.
Embedding our notification widget via amp-iframe won't work
amp-iframe's origin policy doesn't allow same-origin URLs, but we need to load the same-origin URL to access the Notification API permission state.
@cramforce:
We'll disable Safari AMP push support for now
We'll disable Safari support for now since Safari push isn't supported on mobile, but we'll revisit the problem of 3rd party cookies in a iFrame once Safari supports push.
Using cookies to track subscription isn't as accurate as using an iFrame
Using cookies would provide basic subscription tracking, but doesn't take advantage of the much more accurate subscription tracking offered by the Notifications, Push, and Service Worker APIs.
For example, if using cookies to track subscription state, a visitor subscribed to web push notifications who clears their cookies will be prompted to resubscribe (because the cookie would
be our only check for an AMP push subscription), even though clearing cookies doesn't impact the user's actual push subscription.
Instead, we can more accurately and directly check the components of a push subscription. For example, in our implementation, we check whether the service worker registration is active, notification permissions are granted, and the push token is not null. This check directly reflects the actual subscription state.
Popup content should minimally display the permission prompt
Sorry the demo wasn't online at the time (should be up now!). The popup exists primarily to show the notification request, which can't be requested from within an iFrame. Developers and push vendors can customize the subscription popup (which lives on the canonical origin) with extra text.
This sounds good to me. I'd definitely prefer if this component was vendor independent.
Let's move forward, but let's also plan for some time to iterate on the final design during implementation.
Pinging @jasonpang @sebastianbenz @cramforce @ericlindley-g
As another vendor of push notification services, Mobify have been looking at this and we'd like to contribute.
Notification.permission and call PushManager.getSubscription() on the canonical site, and an iframe seems like the only way to do this.isSubscribedToPushNotifications. It's likely that other implementations (such as ours) will need more data (for example, we record different notification sources that the user may be subscribed to). Maybe this 'check' call should return an object that can contain arbitrary metadata? Also, the subscription state might be better represented with isSubscribed and canSubscribe booleans.Additional: it's probably worth noting that the subscribe popup would also have to register and install the service worker in order to set up a subscription.
@benlast
We found a somewhat vendor independent approach (to end users), with the following benefits:
Developers don't need to write custom JavaScript code
They still need to add files (that we provide) to their site (no way around this due to same-origin push policy), but we provide these files without requiring any extra modifications. Better yet, custom modifications (displaying supporting text or graphs with the browser's permission request to encourage subscription) is still possible, just not required.
In our original impl., users would have had to consult their push vendor's docs for a special set of files.
The end-user visible subscription process is significantly faster (popup closes immediately after granting permission vs. waiting seconds)
This is possible because we're outsourcing the actual subscription to the service worker to run in the background, instead of subscribing inside the popup and waiting for the operation to complete.
The disadvantage is that this shifts the complexity to push vendors, which would have to allow subscribing through the service worker.
Our new approach outsources vendor-specific code to the vendor's service worker (which must always be installed anyways as part of web push), which already runs other vendor-specific code. Push vendors must subscribe for push in the service worker and broadcast a signal to controlled window clients when complete.
This way, the process looks like:
Notification.requestPermission() (merged with Push API permission, does not require SW to be active). The browser's notification permission request appears.self.registration.pushManager.subscribe())self.clients.matchAll() ... client.postMessage())This old way requires different script files on the canonical origin for each push vendor. It also makes the popup open for longer (to register the worker and make the subscription call).
@benlast Forgot to reply to extra values/state for isSubscribedToPushNotifications() (your comment #2).
Instead of the iFrame telling the AMP extension whether the user is subscribed or can be subscribed, the iFrame actually only tells the widget whether to _show_ the widget. This way, any custom logic for different use cases can be combined to make a final decision on the widget visibility.
We were thinking: if a widget is visible, it can be clicked and the user will be subscribed. If the user shouldn't be subscribed for any reason (e.g. already subscribed, blocked, dismissed multiple times), the widget shouldn't be visible.
We're thinking of holding a design review on 7/5. Would you like to join the review to discuss all this more?
I'll paste a design document on the 7/5 Design Review soon.
@benlast I put up the design doc for the 7/5 design review.
@jasonpang yes, I'd like to join the review.
@jasonpang a more detailed version of what I was thinking as discussed in the meeting today.
// amp-webpush-subscribe-widget is restricted to fixed-size layouts only
// (e.g. no layout=container) so no page jumps are possible.
<amp-webpush-subscribe-widget>
<div notsubscribed>
Click here to subscribe.
</div>
<div subscribed>
You are subscribed to notifications, if you like to unsubscribe, use browser settings
(or if we support unsubscribe ever, this can be "click here to unsubscribe")
</div>
</amp-webpush-subscribe-widget>
<div subscribed> is optional, if not provided, <div notsubscribed> will simply become hidden and widget becomes empty. Ideally we can detect whether we can also collapse the whole <amp-webpush-subscribe-widget> when it is empty (e.g. if not inside viewport or in a position fixed parent)
I spoke to @beverloo regarding Chrome potentially tightening up various requirements around requesting permission (see https://github.com/WICG/interventions/issues/49).
To summarize:
<amp-webpush> component are not unique to AMP, and Chrome are very keen to make this split-domain approach work in a reasonable way.So, however <amp-webpush> is implemented, and whatever its API, I think the component will need to be prepared to live with these platform-enforced constraints, and a less-than-ideal UX.
@ithinkihaveacat
Regarding your comment on "experimenting" with preventing sites with a low engagement score from seeking notification permissions..
What exactly does "experimenting" mean in this context? Is this something that'll look to be actively enforced and turned on in M62/M61?
@henryh15 I don't have the details but "experimenting" is likely to mean that a small proportion of users (in the stable channel) will get the new behavior.
Hey All @jasonpang and the rest of the involved users, the community is looking forward to having this. Let's make this happen and keep us updated.
Regards,
Ahmed
This issue seems to be in Pending Triage for awhile. @rudygalfi Please triage this to an appropriate milestone.