requestStorageAccessFor API

Draft Community Group Report,

This version:
https://github.com/privacycg/requestStorageAccessFor
Issue Tracking:
GitHub
Inline In Spec
Editors:
(Google)
(Google)

Abstract

The requestStorageAccessFor API allows top-level sites to request access to cross-site cookies on behalf of embedded origins.

Status of this document

This specification is intended to be merged into the HTML Living Standard. It is neither a WHATWG Living Standard nor is it on the standards track at W3C.

It was published by the Privacy Community Group. 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.

Many User Agents prevent content from accessing non-same site data stored in cookies. This can break embedded content which relies on having access to non-same site cookies.

The requestStorageAccessFor API enables developers to request access to non-same site cookies for embedded resources such as iframes, scripts, or images. It accomplishes this by specifying requestStorageAccessFor(requestedOrigin), which allows traversable navigables to request access to unpartitioned cookies on behalf of another origin.

2. Infrastructure

This specification depends on the Infra standard. [INFRA]

3. The requestStorageAccessFor API

This specification defines a method that can be used to request access to unpartitioned data on behalf of another origin (requestStorageAccessFor(requestedOrigin)).

Alex visits https://social.example/. The page sets a cookie. This cookie has been set in a first-party-site context.

Later on, Alex visits https://video.example/, which has an img in it which loads https://social.example/profile-image. In this case, the social.example Document doc is in a third party context, and the cookie set previously might or might not be visible from doc.cookie, depending on User Agent storage access policies.

A script on https://video.example/ could request access on behalf of https://social.example by calling doc.requestStorageAccessFor(requestedOrigin) with USVString requestedOrigin as https://social.example.

Note: the circumstances for use of the access have to be limited to those cases where the requested origin opts into sharing. More information is available in § 7 Privacy considerations and § 8 Security considerations.

Unpartitioned data is client-side storage that would be available to a site were it loaded in a first-party-site context.

A Document is in a first-party-site context if it is the active document of a traversable navigable. Otherwise, it is in a first-party-site context if it is an active document and the origin and top-level origin of its relevant settings object are same site with one another.

A Document is in a third party context if it is not in a first-party-site context.

3.1. Changes to Document

partial interface Document {
  Promise<undefined> requestStorageAccessFor(USVString requestedOrigin);
};
When invoked on Document doc with USVString requestedOrigin, the requestStorageAccessFor(requestedOrigin) method must run these steps:
  1. Let p be a new promise.

  2. If doc is not fully active, then reject p with an "InvalidStateError" DOMException and return p.

  3. If doc’s node navigable is not a traversable navigable, reject p with an "NotAllowedError" DOMException and return p.

  4. If doc’s origin is an opaque origin, reject p with an "NotAllowedError" DOMException and return p.

  5. If doc’s relevant global object is not a secure context, then reject p with a "NotAllowedError" DOMException and return p.

  6. Let parsedURL be the the result of running the URL parser on requestedOrigin.

  7. If parsedURL is failure, reject p with a TypeError and return p.

  8. Let origin be parsedURL’s origin.

  9. If origin is an opaque origin, reject p with an "NotAllowedError" DOMException and return p.

  10. If doc’s origin is same origin with origin, resolve and return p.

  11. Let descriptor be a newly created TopLevelStorageAccessPermissionDescriptor with name set to "top-level-storage-access" and with requestedOrigin set to origin.

  12. Let has activation be true if doc’s Window object has transient activation, and false otherwise.

  13. Run these steps in parallel:

    1. Let settings be doc’s relevant settings object.

    2. Let global be doc’s relevant global object.

    3. Let existing state be descriptor’s permission state with settings.

    4. If existing state is granted:

      1. Queue a global task on the permissions task source given global to resolve p.

      2. Return.

    5. If existing state is denied:

      1. If doc’s Window object has transient activation, consume user activation with it.

      2. Queue a global task on the permissions task source given global to reject p with a "NotAllowedError" DOMException.

      3. Return.

    6. Assert that doc’s node navigable is a traversable navigable.

    7. If has activation is false:

      1. Queue a global task on the permissions task source given global to reject p with a n "NotAllowedError" DOMException.

      2. Return.

    8. Let permissionState be the result of requesting permission to use "top-level-storage-access" with descriptor.

      NOTE: Note that when requesting permissions and deciding whether to show a prompt, user agents apply implementation-defined behavior to shape the end user experience. Particularly for top-level-storage-access, user agents are known to apply custom rules that will grant or deny a permission without showing a prompt.

    9. If permissionState is granted:

      1. Queue a global task on the permissions task source given global to resolve p.

      2. Return.

    10. If doc’s Window object has transient activation, consume user activation with it.

    11. Queue a global task on the permissions task source given global to reject p with a "NotAllowedError" DOMException.

  14. Return p.

The permissions task source shouldn’t be used directly. [Issue #privacycg/requestStorageAccessFor#15]

3.2. User Agent top-level storage access policies

To determine if a request has top-level storage access with request request, run these steps:
  1. Let settings be request’s client's relevant global object's relevant settings object.

  2. Let embedded origin be request’s url's origin.

  3. Let descriptor be a newly created TopLevelStorageAccessPermissionDescriptor with name set to "top-level-storage-access" and with requestedOrigin set to embedded origin.

  4. Let existing state be descriptor’s permission state with settings.

  5. If existing state is granted, return true.

  6. Return false.

4. Permissions Integration

The requestStorageAccessFor API defines a powerful feature identified by the name "top-level-storage-access". It defines the following permission-related algorithms:

PermissionDescriptor
The "top-level-storage-access" powerful feature defines a PermissionDescriptor as follows:
dictionary TopLevelStorageAccessPermissionDescriptor : PermissionDescriptor {
    USVString requestedOrigin = "";
};
permission query algorithm
To query the "top-level-storage-access" permission, given a PermissionDescriptor permissionDesc and a PermissionStatus status, run the following steps:
  1. Set status’s state to permissionDesc’s permission state.

  2. If status’s state is denied, set status’s state to prompt.

    Note: The denied permission state is not revealed to avoid exposing the user’s decision to developers. This is done to prevent retaliation against the user and repeated prompting to the detriment of the user experience.

permission key type
A permission key of the "top-level-storage-access" feature has the type site.

Note: the requestedOrigin field ensures that the permission store entry is double-keyed.

permission key generation algorithm
To generate a new permission key for the "top-level-storage-access" feature, given an environment settings object settings, run the following steps:
  1. Let current origin be settingsorigin.

  2. If current origin is not same site with settingstop-level origin, return null.

  3. Return the result of obtaining a site from settingstop-level origin.

    Note: the check for whether settingsorigin is same site with settingstop-level origin is intended to disallow permission queries from cross-site frames. This depends on the invariant that top-level-storage-access permission requests are only allowed in a top-level browsing context. As such, this check is only relevant in query(permissionDesc).

permission key comparison algorithm
To compare permission keys key1 and key2 for the "top-level-storage-access" feature, run the following steps:
  1. If key1 is null or key2 is null, return false.

  2. Return key1 is same site with key2.

5. Fetch Integration

The requestStorageAccessFor(requestedOrigin) only directly affects cookie behavior on subresource requests made from top-level documents to the requested origin.

In http network or cache fetch, when determining whether to block cookies, run the following algorithm. A true result means cookies can be unblocked:
  1. Let has top-level access be the result of running determine if a request has top-level storage access on request.

  2. If has top-level access is false, return false.

  3. Let is subresource be true if request is a subresource request and false otherwise.

  4. Let allowed subresource mode be true if request’s mode is "cors" and request’s credentials mode is "include", and false otherwise.

  5. If is subresource is true and allowed subresource mode is false, return false.

  6. If request’s client's relevant global object's associated document is not a traversable navigable, return false.

  7. Return true.

6. Storage Access API Integration

Note: even after a successful requestStorageAccessFor(requestedOrigin) call, frames have to explicitly invoke requestStorageAccess() for cookie access. This modification allows requestStorageAccessFor(requestedOrigin) to allow resolution of requestStorageAccess() calls similarly to a prior successful requestStorageAccess() grant.

Modify requestStorageAccess() to insert the following steps before step 13.4 (i.e. before checking transient activation):
  1. Let settings be doc’s relevant settings object.

  2. Let origin be settingsorigin.

  3. Let descriptor be a newly created TopLevelStorageAccessPermissionDescriptor with name set to "top-level-storage-access" and with requestedOrigin set to origin.

  4. If descriptor’s permission state is granted, queue a global task on the permissions task source given global to resolve p, and return.

  5. If descriptor’s permission state is denied, queue a global task on the permissions task source given global to reject p with a "NotAllowedError" DOMException, and return.

7. Privacy considerations

Like the [STORAGE-ACCESS], requestStorageAccessFor(requestedOrigin) is intended to enable removal of cross-site cookies. It enables developers to re-gain cross-site cookies with additional constraints.

Note: many of the same considerations as in The Storage Access API § 6 Privacy considerations apply. This section primarily covers the differences.

requestStorageAccess() requires interaction with an embedded document. By requiring interaction only with the top-level document, requestStorageAccessFor(requestedOrigin) lowers the bar for a potential prompt, though embedded documents can also be quite prominent (or use other techniques to get user interaction). Implementation-defined acceptance and rejection steps are intended to allow user agents to reject abusive requests based on logic they see fit. The prompts used have to be careful to indicate the direction of the request, such that the user is able to understand who is requesting access.

As with requestStorageAccess(), the same tension between user consent and prompt fatigue exists with requestStorageAccessFor(requestedOrigin); much like the Storage Access API, implementation-defined acceptance and rejection steps are intended to enable implementers with differing stances on this question to make compromises as they see fit.

Another difference is that queries for the permission can be more sensitive, depending on the context. Note that a frame has to be unable to request the state of either of:

In the former case, this would allow bogus domains (or combinations thereof) to be used as identifiers; in the latter case, it would reveal state under unrelated origins.

8. Security considerations

It is important that requestStorageAccessFor(requestedOrigin) not degrade security properties of the web platform, even when compared to post-removal of cross-site cookies. Third-party cookie removal has potential benefits for security, specifically in mitigating attacks that rely upon authenticated requests, e.g. CSRF. We do not wish requestStorageAccessFor(requestedOrigin) to be a foothold for such attacks to leverage.

Note: The Storage Access API § 7 Security considerations properties hold for much of this proposal. Specifically, frame-level access is only granted once requestStorageAccess() is successfully invoked. For frame access, requestStorageAccessFor(requestedOrigin) merely simplifies the activation and prompting requirements.

requestStorageAccessFor(requestedOrigin) does expand the scope of concerns in two areas: subresource requests made by the top-level document and potential notification abuse.

8.1. Subresource Requests

The specific security controls proposed by the API are:

Additionally, only requests initiated from the top-level document will be eligible for inclusion of SameSite=None cookies. This ensures that other embedded frames do not receive escalated privileges.

8.2. Notification Abuse

Unlike the [STORAGE-ACCESS], interaction is only required with the top-level document, rather than an embedded document. This does increase the likelihood of prompting.

Like the Storage Access API, user activation is consumed on denial, which prevents repeated requests.

The implementation-defined rejection steps also allow for imposition of numeric limits or denylists for abusive actors.

As mentioned in § 7 Privacy considerations, because of the direction of the request, the language in user agents' prompts should indicate which site initiated the storage access request.

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.

Index

Terms defined by this specification

Terms defined by reference

References

Normative References

[DOM]
Anne van Kesteren. DOM Standard. Living Standard. URL: https://dom.spec.whatwg.org/
[FETCH]
Anne van Kesteren. Fetch Standard. Living Standard. URL: https://fetch.spec.whatwg.org/
[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/
[PERMISSIONS]
Marcos Caceres; Mike Taylor. Permissions. URL: https://w3c.github.io/permissions/
[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
[STORAGE-ACCESS]
Storage Access API. CG Draft. URL: https://privacycg.github.io/storage-access/
[URL]
Anne van Kesteren. URL Standard. Living Standard. URL: https://url.spec.whatwg.org/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL Standard. Living Standard. URL: https://webidl.spec.whatwg.org/

IDL Index

partial interface Document {
  Promise<undefined> requestStorageAccessFor(USVString requestedOrigin);
};

dictionary TopLevelStorageAccessPermissionDescriptor : PermissionDescriptor {
    USVString requestedOrigin = "";
};

Issues Index

The permissions task source shouldn’t be used directly. [Issue #privacycg/requestStorageAccessFor#15]