[Proposal] Modal Window

This proposal came out of discussions with the Web Payments WG at TPAC that also included input from members of the WebAuthn WG.

The explainer is at https://github.com/adrianhopebailie/modal-window/blob/master/explainer.md

Problem

There is a need to showing UI from another origin, in a top-level context, without a complete redirect and loss of user context. However the solution must avoid the various issues with pop-ups and click-jacking that have plagued the use of window.open() and <iframe> in the past.

Background

The Payment Handler specification proposes an API to render a URL inside a modal window in order to provide a mechanism for a Payment Handler to show UI.

Clever use of this API demonstrated the general usefulness of this feature for other use cases such as Single-Sign-On and Web Share.

Solution

The proposal is a new API that allows a website to request a modal pop-up window. Only a single modal can be open at a time and communication between the windows will be via PostMessage.

Opener Context (Window or Worker)

const modalWindow = await window.openModal(
    'https://authorization-server.com/auth?response_type=code&scope=photos&state=1234zyx');
// modalWindow is an instance of Window (https://developer.mozilla.org/en-US/docs/Web/API/Window)
window.addEventListener('message', (e) => {
  // Check origin
  if ( e.origin === 'https://authorization-server.com' ) {
      // Retrieve data sent in postMessage
      const data = e.data;
      // Send reply to source of message
      e.source.postMessage('some reply', e.origin);
  }
}, false);
modalWindow.postMessage('some message', 'https://authorization-server.com');

Modal Window Context

window.addEventListener('message', (e) => {
  // Check parent origin is for a valid client
  const client = getClientFromOrigin(e.origin)
  if ( client ) {
      // Retrieve data sent in postMessage
      const data = e.data;
      // Send reply to source of message
      e.source.postMessage('some reply', e.origin);
  }
}, false);

window.modalParent.postMessage('some message', '*');
2 Likes

Interesting. Reading the linked issues and specifications to open a modal requires user activation

    1. only triggered by user activation.
  • Website triggers event E.
  • If event’s isTrusted attribute is false, return a Promise rejected with a " InvalidStateError " DOMException.
  • If this algorithm is not triggered by user activation, return a promise rejected with an " InvalidAccessError " DOMException .
  • Unfortunately this is towards impossible. In an asynchronous execution environment like JavaScript you can’t really link a call to openWindow with a “payment” event unless it’s done synchronously.

Also this proposal and the explainer use the term “Worker” not exclusively “ServiceWorker”.

Questions:

  1. Given than window is undefined at a DedicatedWorkerGlobalScope does this proposal define window at a Worker re “Opener Context (Window or Worker)” and the code const modalWindow = await window.openModal?

  2. Is this proposal exclusive to Payment Handler API and ServiceWorker or can any API utilize a Worker or SharedWorker to open a window?

  3. Precisely how does a user activation get transferred to a Worker?

  4. At Chrome, Chromium user activation is currently bounded within a strict 5 seconds (see Issue 1008065: Consider different criterion as user activation); given the asynchronous code involved how does this proposal intend to guarantee the “transfer” of a user activation to a ServiceWorker (or Worker) and open a window within 5 seconds?