Capability Delegation

Draft Community Group Report,

This version:
https://wicg.github.io/capability-delegation/spec.html
Issue Tracking:
GitHub
Editor:
(Google Canada)

Abstract

Transferring the ability to use restricted APIs to another window.

Status of this document

This specification was published by the Web Platform Incubator Community Group. It is not a W3C Standard nor is it on the W3C Standards Track. Please note that under the W3C Community Contributor License Agreement (CLA) there is a limited opt-out and other conditions apply. Learn more about W3C Community and Business Groups.

1. Introduction

This section is non-normative.

This specification defines a mechanism through which a script can delegate its ability to call a restricted API to another browsing context it trusts. The focus here is a dynamic delegation mechanism that exposes the delegated capability to the target browsing context in a time-constrained manner.

1.1. What is capability delegation?

Many capabilities in the Web are usable from JS in restricted manners. For example:

Capability delegation means allowing a frame to dynamically relinquish its ability to call a restricted API and transfer the ability to another (sub)frame it can trust. The word "dynamic" here means the effect of the delegation lasts for a limited time as defined by the capability being delegated. This is different from static (load-time) exposure of a capability to a browsing context through iframe allow attribute where the capability becomes exposed to a subframe in a time-unconstrained manner.

1.2. Initiating a delegation vs using a capability

Capability delegation needs two distinct steps to be effective. The first step is "initiation" where one browsing context notifies another browsing context about a specific capability being delegated. After initiation, the second (i.e. the receiver) browsing context would "use" the delegated capability, which typically means calling a capability-defined method. While the capability delegation specification here does not define the API interface used in the second step, it redefines the API’s internal behavior.

Because of this, this specification consists of two distinct parts: defining an API for the initiation step, and then defining delegated behavior for one specific "user" API. For the second part, this specification focuses on behavior changes needed in Payment Request API, which would serve as a guide for similar changes in any other APIs that would utilize capability delegation in future.

1.3. Transient availability

Both the steps mentioned above are time-constrained in nature:

  1. The initiation step is activation consuming, so the step is allowed only after a recent user activation. Moreover, the consumption of user activation here guarantees that the delegation mechanism can’t be used more than once per user activation. This prevents malicous uses of capability delegation, like repeated delegation attempts to multiple frames to effectively bypass the user activation restriction for the delegated API.

  2. After a successful completion of the initiation step, the delegated API becomes available for use in the target browsing context for a few seconds only. The exact time limit here depends on how a delegated API defines the delegated behavior in its own specification. For an API that does not define its own time limit, the default limit will be the same as user activation expiry.

2. Examples

When a site wants to delegate the capability to call [payment-request] show() from a subframe after a mouse click, it will post a message to the subframe with an additional option to specify the delegated capability:
window.onclick = () => {
  targetWindow.postMessage('a_message', {delegate: "payment"});
};

Upon receiving the message, the subframe would be able to use show() even though the frame hasn’t received a user activation:

window.onmessage = () => {
  const payRequest = new PaymentRequest(...);
  const payResponse = await payRequest.show();
  ...
}

3. Initiating capability delegation

When a browsing context wants to delegate a capability to another browsing context, it posts a message to the second browsing context with an extra WindowPostMessageOptions called delegate specifying the capability. The value of this option MUST be a feature-identifier. The option MUST be ignored if the value does not correspond to any features supported by the user agent.

A list of possible feature-identifier values appears here.

3.1. Monkey-patch to HTML spec

The WindowPostMessageOptions IDL definition will include an additional field as follows:

DOMString? delegate;

In the algorithm for window post message, the following step:

  1. Let transfer be options["transfer"].

will be followed by two additional steps as follows:

  1. Let delegate be options["delegate"].

  2. If delegate is not null, then:

    1. If the user agent does not support delegating the feature indicated by delegate, then throw a "NotSupportedError" DOMException.

    2. If targetWindow’s associated Document is not allowed-to-use the feature indicated by delegate, then throw a a "NotAllowedError" DOMException.

    3. If targetOrigin is a single U+002A ASTERISK character (*), then throw a a "NotAllowedError" DOMException.

      The default value of targetOrigin is "/", restricting the message to same-origin targets. The additional requirement to use a string other than "*" means that cross-origin messages has to specify the specific origin for which they are intended.
    4. Let source be incumbentSettings’s global object.

    5. If source does not have transient activation, then throw a "NotAllowedError" DOMException.

    6. Consume user activation in source.

4. Tracking delegated capability

Capabilities delegated to a browsing context will be tracked using a map named Window.DELEGATED_CAPABILITY_TIMESTAMPS. Each time a capability is delegated to a Window, an entry will be added in DELEGATED_CAPABILITY_TIMESTAMPS with a key equal to the feature-identifier representing the capability, and a value equal to current DOMHighResTimeStamp. If the map already has an entry for the same key, the existing value will be updated to current DOMHighResTimeStamp.

4.1. Monkey-patch to HTML spec

Right before the algorithm for window post message, a new paragraph will be inserted, as follow:

For the purpose of tracking capabilities delegated to a browsing context, each Window has a map called DELEGATED_CAPABILITY_TIMESTAMPS from feature-identifier to DOMHighResTimeStamp. The map is initialized with an empty map.

In the algorithm for window post message, two additional sub-steps will be added to current Step 8. The first additional sub-step will be inserted after the following sub-step:

  1. Queue a global task ...

    1. Let origin be the serialization of incumbentSettings’s origin.

as follows:

  1. Queue a global task ... (unchanged)

    1. Let delegate be options["delegate"].

The second additional sub-step will be inserted after the following sub-step:

  1. Queue a global task ...

    1. Let newPorts be a new frozen array consisting of ...

as follows:

  1. Queue a global task ... (unchanged)

    1. Let newPorts be a new frozen array consisting of ... (unchanged except for numbering)

    2. If delegate is not null, AND the user agent supports delegating delegate, then set DELEGATED_CAPABILITY_TIMESTAMPS[delegate] to current high resolution time.

5. Defining delegated capability behavior

Any capability that defines a delegated behavior uses the corresponding entry in Window.DELEGATED_CAPABILITY_TIMESTAMPS in a manner appropriate for the capability. Below is the spec change needed for one particular capability.

5.1. Monkey-patch to Payment Request spec

In the algorithm for show(), the following steps will be replaced to implement the delegated behavior:

The two steps:

  1. If the relevant global object of request does not have transient activation:

    1. Return a promise rejected with a "SecurityError" DOMException.

  2. Consume user activation of the relevant global object.

will be replaced by the following three steps:

  1. If the relevant global object of request does not have transient activation, AND the timestamp DELEGATED_CAPABILITY_TIMESTAMPS["payment"] in the relevant global object is either undefined or expired:

    1. Return a promise rejected with a "SecurityError" DOMException.

  2. If the relevant global object of request does not have transient activation, then clear the map entry DELEGATED_CAPABILITY_TIMESTAMPS["payment"].

  3. Otherwise, consume user activation of the relevant global object.

5.2. Monkey-patch to Fullscreen spec

In the algorithm for requestFullscreen(), the following changes will be done to implement the delegated behavior:

The last condition in Step 5:

  1. If any of the following conditions are false, then set error to true: ...

    • This’s relevant global object has transient activation or the algorithm is triggered by a user generated orientation change.

will be replaced by:

  1. If any of the following conditions are false, then set error to true: ... (unchanged)

    • This’s relevant global object has transient activation, or the timestamp DELEGATED_CAPABILITY_TIMESTAMPS["fullscreen"] in this’s relevant global object is neither undefined nor expired, or the algorithm is triggered by a user generated orientation change.

Right before the Step 10:

  1. Let fullscreenElements be an ordered set initially consisting of this.

the following new step will be inserted:

  1. If this’s relevant global object does not have transient activation, then clear the map entry DELEGATED_CAPABILITY_TIMESTAMPS["fullscreen"] in this’s relevant global object.

  2. Let fullscreenElements be an ordered set initially consisting of this. (unchanged except for numbering)

5.3. Monkey-patch to [SCREEN-CAPTURE] spec

In the algorithm for getDisplayMedia(), the following changes will be done to implement the delegated behavior:

The condition in Step 3:

  1. If the relevant global object of this does not have transient activation, return a promise rejected with a DOMException object whose name attribute has the value InvalidStateError.

will be replaced by:

  1. If the relevant global object of this does not have transient activation AND the timestamp DELEGATED_CAPABILITY_TIMESTAMPS["display-capture"] in this’s relevant global object is either undefined or expired, return a promise rejected with a DOMException object whose name attribute has the value InvalidStateError.

Conformance

Document conventions

Conformance requirements are expressed with a combination of descriptive assertions and RFC 2119 terminology. The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in the normative parts of this document are to be interpreted as described in RFC 2119. However, for readability, these words do not appear in all uppercase letters in this specification.

All of the text of this specification is normative except sections explicitly marked as non-normative, examples, and notes. [RFC2119]

Examples in this specification are introduced with the words “for example” or are set apart from the normative text with class="example", like this:

This is an example of an informative example.

Informative notes begin with the word “Note” and are set apart from the normative text with class="note", like this:

Note, this is an informative note.

Conformant Algorithms

Requirements phrased in the imperative as part of algorithms (such as "strip any leading space characters" or "return false and abort these steps") are to be interpreted with the meaning of the key word ("must", "should", "may", etc) used in introducing the algorithm.

Conformance requirements phrased as algorithms or specific steps can be implemented in any manner, so long as the end result is equivalent. In particular, the algorithms defined in this specification are intended to be easy to understand and are not intended to be performant. Implementers are encouraged to optimize.

Index

Terms defined by this specification

Terms defined by reference

References

Normative References

[FULLSCREEN]
Philip Jägenstedt. Fullscreen API Standard. Living Standard. URL: https://fullscreen.spec.whatwg.org/
[HR-TIME-3]
Yoav Weiss. High Resolution Time. URL: https://w3c.github.io/hr-time/
[HTML]
Anne van Kesteren; et al. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra Standard. Living Standard. URL: https://infra.spec.whatwg.org/
[PAYMENT-REQUEST-1.1]
Marcos Caceres; Rouslan Solomakhin; Ian Jacobs. Payment Request API 1.1. URL: https://w3c.github.io/payment-request/
[PERMISSIONS-POLICY-1]
Ian Clelland. Permissions Policy. URL: https://w3c.github.io/webappsec-permissions-policy/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. March 1997. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
[SCREEN-CAPTURE]
Jan-Ivar Bruaroey; Elad Alon. Screen Capture. URL: https://w3c.github.io/mediacapture-screen-share/
[WebCryptoAPI]
Mark Watson. Web Cryptography API. URL: https://w3c.github.io/webcrypto/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL Standard. Living Standard. URL: https://webidl.spec.whatwg.org/

Informative References

[PAYMENT-REQUEST]
Marcos Caceres; Rouslan Solomakhin; Ian Jacobs. Payment Request API. URL: https://w3c.github.io/payment-request/