The Storage Access API

Draft Community Group Report,

This version:
https://privacycg.github.io/storage-access/
Issue Tracking:
GitHub
Inline In Spec
Editors:
(Apple Inc.)
(Apple Inc.)

Abstract

The Storage Access API enables content in iframes to request access to website data (such as cookies).

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.

User Agents sometimes prevent content inside certain iframes from accessing data stored in client-side storage mechanisms like cookies. This can break embedded content which relies on having access to client-side storage.

The Storage Access API enables content inside iframes to request and be granted access to their client-side storage, so that embedded content which relies on having access to client-side storage can work in such User Agents. [STORAGE-ACCESS-INTRO]

2. Infrastructure

This specification depends on the Infra standard. [INFRA]

3. The Storage Access API

This specification defines a method to query whether or not a Document currently has access to its unpartitioned data (hasStorageAccess()), and a method that can be used to request access to its unpartitioned data (requestStorageAccess()).

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 iframe on it which loads https://social.example/heart-button. 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.

Script in the iframe can call doc.hasStorageAccess() to determine if it has access to the cookie. If it does not have access, it can request access by calling doc.requestStorageAccess().

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 top-level browsing context. 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.

If we let nested iframes use this API, we may have to revisit these definitions. <https://github.com/privacycg/storage-access/issues/10>

3.1. User Agent state related to storage access

A storage access map is a map whose keys are partitioned storage keys and whose values are storage access flag sets.

User Agents maintain a single global storage access map.

What is the lifecycle of the global storage access map? How long do we remember its contents? Firefox and Safari differ here. <https://github.com/privacycg/storage-access/issues/2>

When do we age out entries in the global storage access map? See also Scope of Storage Access. <https://github.com/privacycg/storage-access/issues/5>

Each agent cluster has a storage access map.

When an agent cluster is created, its storage access map is initialized with a clone of the global storage access map.

To obtain the storage access map for a Document doc, run the following steps:

  1. Return the storage access map of doc’s relevant agent's agent cluster.

A partitioned storage key is a tuple consisting of a top-level site and an embedded site (both sites).

(("https", "news.example"), ("https", "social.example")) is a partitioned storage key whose top-level site is ("https", "news.example") and whose embedded site is ("https", "social.example").

To generate a partitioned storage key for a Document doc, run the following steps:

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

  2. Let site be the result of obtaining a site from settingsorigin.

  3. If doc’s browsing context is a top-level browsing context, return the partitioned storage key (site, site).

  4. Let top-level site be the result of obtaining a site from settingstop-level origin.

  5. Return the partitioned storage key (top-level site, site).

A storage access flag set is a set of zero or more of the following flags, which are used to gate access to client-side storage for embedded site when loaded in a third party context on top-level site:

The has storage access flag

When set, this flag indicates embedded site has access to its unpartitioned data when it’s loaded in a third party context on top-level site.

The was expressly denied storage access flag

When set, this flag indicates that the user expressly denied embedded site access to its unpartitioned data when it’s loaded in a third party context on top-level site.

To obtain a storage access flag set for a partitioned storage key key from a storage access map map, run the following steps:

  1. If map[key] does not exist, run these steps:

    1. Let flags be a new storage access flag set.

    2. Set map[key] to flags.

  2. Return map[key].

To save the storage access flag set for a partitioned storage key key in a storage access map map, run the following steps:

  1. Set global storage access map[key] to map[key].

3.2. Changes to Document

partial interface Document {
  Promise<boolean> hasStorageAccess();
  Promise<undefined> requestStorageAccess();
};

When invoked on Document doc, the hasStorageAccess() method must run these steps:

  1. Let p be a new promise.

  2. Let map be the result of obtaining the storage access map for doc.

  3. Let key be the result of generating a partitioned storage key from doc.

  4. If key is failure, then resolve p with false and return p.

  5. Let flag set be the result of obtaining the storage access flag set with key from map.

  6. If flag set’s was expressly denied storage access flag is set, resolve p with false and return p.

  7. If doc’s origin is an opaque origin, resolve p with false and return p.

  8. If doc’s browsing context is a top-level browsing context, resolve p with true and return p.

  9. Let topDoc be the active document of doc’s browsing context's top-level browsing context.

  10. If doc is same origin with topDoc, resolve p with true and return p.

  11. Let key be the result of generating a partitioned storage key from doc.

  12. Resolve p with the result of running determine if a site has storage access with key and doc.

  13. Return p.

Shouldn’t step 7 be same site?

When invoked on Document doc, the requestStorageAccess() method must run these steps:

  1. Let p be a new promise.

  2. Enqueue the following steps on the permission task source:

    1. Let map be the result of obtaining the storage access map for doc.

    2. Let key be the result of generating a partitioned storage key from doc.

    3. If key is failure, reject p.

    4. Let flag set be the result of obtaining the storage access flag set with key from map.

    5. If flag set’s was expressly denied storage access flag is set, reject p.

    6. If flag set’s has storage access flag is set, resolve p.

    7. If doc’s origin is an opaque origin, reject p.

    8. If doc’s browsing context is a top-level browsing context, resolve p.

    9. Let topDoc be the active document of doc’s browsing context's top-level browsing context.

    10. If doc is same origin with topDoc, resolve p.

    11. If doc’s active sandboxing flag set has its sandbox storage access by user activation flag set, reject p.

    12. If doc’s browsing context's parent browsing context is not a top-level browsing context, reject p.

    13. If this algorithm was invoked when doc’s Window object did not have transient activation, reject p.

    14. Determine the storage access policy with key, doc, and p.

    15. Set flag set’s has storage access flag.

    16. Save the storage access flag set for key in map.

    17. Resolve p.

  3. Return p.

Shouldn’t step 3.7 be same site?

Remove step 3.9 if we determine that nested iframes should be able to request storage access. <https://github.com/privacycg/storage-access/issues/10>

3.2.1. User Agent storage access policies

Different User Agents have different policies around whether or not sites may access their unpartitioned data when they’re in a third party context. User Agents check and/or modify these policies when client-side storage is accessed (see § 3.4 Changes to various client-side storage mechanisms) as well as when hasStorageAccess() and requestStorageAccess() are called.

To determine if a site has storage access with partitioned storage key key and Document doc, run these steps:

  1. Let map be the result of obtaining the storage access map for doc.

  2. Let flag set be the result of obtaining the storage access flag set with key from map.

  3. If flag set’s has storage access flag is set, return true.

  4. Let has storage access (a boolean) be the result of running an implementation-defined set of steps to determine if key’s embedded site has access to its unpartitioned data on key’s top-level site.

  5. If has storage access is true, set flag set’s has storage access flag.

  6. Save the storage access flag set for key in map.

  7. Return has storage access.

To determine the storage access policy for partitioned storage key key with Document doc and Promise p, run these steps:

  1. Let map be the result of obtaining the storage access map for doc.

  2. Let flag set be the result of obtaining the storage access flag set with key from map.

  3. Let implicitly granted and implicitly denied (each a boolean) be the result of running an implementation-defined set of steps to determine if key’s embedded site's request for storage access on key’s top-level site should be granted or denied without prompting the user.

    Note: These implementation-defined set of steps might result in flag set’s has storage access flag and was expressly denied storage access flag changing, since the User Agent could have relevant out-of-band information (e.g. a user preference that changed) that this specification is unaware of.

  4. If implicitly granted is true, resolve p and return.

  5. If implicitly denied is true, reject p and return.

  6. Ask the user if they would like to grant key’s embedded site access to its unpartitioned data when it’s loaded in a third party context on key’s top-level site, and wait for an answer. Let expressly granted and expressly denied (both booleans) be the result.

    Note: While expressly granted and expressly denied cannot both be true, they could both be false in User Agents which allow users to dismiss the prompt without choosing to allow or deny the request. (Such a dismissal is interpreted in this algorithm as a denial.)

  7. If expressly granted is true, run these steps:

    1. Unset flag set’s was expressly denied storage access flag.

    2. Save the storage access flag set for key in map.

    3. Resolve p and return.

  8. Unset flag set’s has storage access flag.

  9. If expressly denied is true, run these steps:

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

    2. Set flag set’s was expressly denied storage access flag.

  10. Save the storage access flag set for key in map.

  11. Reject p and return.

since this is UA-defined, does it make sense to follow-up separately with a user prompt?

Before changing the current entry of a session history, run the following steps:

  1. Let doc be current entry's Document.

  2. Let map be the result of obtaining the storage access map for doc’s browsing context's top-level browsing context.

  3. Let key be the result of generating a partitioned storage key from doc.

  4. If key is failure, abort these steps.

  5. Let flag set be the result of obtaining the storage access flag set with key from map.

  6. Unset flag set’s has storage access flag.

  7. Save the storage access flag set for key in map.

What this section should look like ultimately hinges on <https://github.com/privacycg/storage-access/issues/3>

3.4. Changes to various client-side storage mechanisms

Write this section. For each kind of client-side storage affected, modify them to invoke determine if a site has storage access & modify their behavior based on the result.

Should this API affect client-side storage other than cookies? <https://github.com/privacycg/storage-access/issues/4>

3.4.1. Cookies

Write this section.

3.5. Sandboxing storage access

A sandboxing flag set has a sandbox storage access by user activation flag. This flag prevents content from requesting storage access.

To the parse a sandboxing directive algorithm, add the following under step 3:

What about Feature Policy? <https://github.com/privacycg/storage-access/issues/12>

4. Privacy considerations

Write this section.

A modal dialog box which states 'Do you want to allow “video.example” to use cookies and website data while browsing “news.example”? This will allow “video.example” to track your activity.' and which has two buttons, “Don’t Allow” and “Allow”.
An example prompt which could be shown to the user when a site calls document.requestStorageAccess().

5. Security considerations

Write this section.

6. Automation

For the purposes of user-agent automation and application testing, this document defines the following extension command for the [WebDriver] specification.

6.1. Set Storage Access

HTTP Method URI Template
POST /session/{session id}/storageaccess

The Set Storage Access extension command modifies the storage access policy for the current browsing context.

The remote end steps are:

  1. Let blocked be the result of getting a property from parameters named blocked.

  2. If blocked is not a boolean return a WebDriver error with WebDriver error code invalid argument.

  3. Let origin be the result of getting a property from parameters named origin.

  4. If origin is not a single U+002A ASTERISK character (*), then:

    1. Let parsedURL be the the result of running the URL parser on origin.

    2. If parsedURL is failure, then return a WebDriver error with WebDriver error code invalid argument.

    3. Set origin to parsedURL’s origin.

  5. If the current browsing context is not a top-level browsing context return a WebDriver error with WebDriver error code unsupported operation.

  6. Let doc be the current browsing context's active document.

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

  8. Let top-level site be the result of obtaining a site from settings’s origin.

  9. If origin is a single U+002A ASTERISK character (*), then:

    1. If blocked is true, then:

      1. Run an implementation-defined set of steps to ensure that no site has access to its unpartitioned data when loaded in a third party context on top-level site.

    2. Otherwise, if blocked is false, then:

      1. Run an implementation-defined set of steps to ensure that any site has access to its unpartitioned data when loaded in a third party context on top-level site.

  10. Otherwise:

    1. Let embedded site be the result of obtaining a site from origin.

    2. If embedded site is same site with top-level site return a WebDriver error with WebDriver error code unsupported operation.

    3. If blocked is true, then:

      1. Run an implementation-defined set of steps to ensure that embedded site does not have access to its unpartitioned data when loaded in a third party context on top-level site.

    4. Otherwise, if blocked is false, then:

      1. Run an implementation-defined set of steps to ensure that embedded site has access to its unpartitioned data when loaded in a third party context on top-level site.

  11. If the above implementation-defined step of steps resulted in failure, return a WebDriver error with WebDriver error code unknown error.

  12. Return success with data null.

Acknowledgements

Many thanks to Anne van Kesteren, Ben Kelly, Brad Girardeau, Brad Hill, Brady Eidson, Brandon Maslen, Chris Mills, Dave Longley, Domenic Denicola, Ehsan Akhgari, Geoffrey Garen, Jack Frankland, James Coleman, James Hartig, Jeffrey Yasskin, Kushal Dave, Luís Rudge, Maciej Stachowiak, Matias Woloski, Mike O’Neill, Mike West, Pete Snyder, Rob Stone, Stefan Leyhane, Steven Englehardt, Theresa O’Connor, Travis Leithead, Yan Zhu, Zach Edwards, and everyone who commented on whatwg/html#3338, privacycg/proposals#2, and privacycg/storage-access/issues for their feedback on this proposal.

Thanks to the WebKit Open Source Project for allowing us to use the Storage Access API Prompt image, which was originally published on webkit.org.

Conformance

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/
[ECMASCRIPT]
ECMAScript Language Specification. URL: https://tc39.es/ecma262/
[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]
Mounir Lamouri; Marcos Caceres; Jeffrey Yasskin. Permissions. 20 July 2020. WD. URL: https://www.w3.org/TR/permissions/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. March 1997. Best Current Practice. URL: https://tools.ietf.org/html/rfc2119
[URL]
Anne van Kesteren. URL Standard. Living Standard. URL: https://url.spec.whatwg.org/
[WebDriver]
Simon Stewart; David Burns. WebDriver. 5 June 2018. REC. URL: https://www.w3.org/TR/webdriver1/
[WebIDL]
Boris Zbarsky. Web IDL. 15 December 2016. ED. URL: https://heycam.github.io/webidl/

Informative References

[STORAGE-ACCESS-INTRO]
John Wilander. Introducing Storage Access API. February 2018. Blog post. URL: https://webkit.org/blog/8124/introducing-storage-access-api/

IDL Index

partial interface Document {
  Promise<boolean> hasStorageAccess();
  Promise<undefined> requestStorageAccess();
};

Issues Index

If we let nested iframes use this API, we may have to revisit these definitions. <https://github.com/privacycg/storage-access/issues/10>
What is the lifecycle of the global storage access map? How long do we remember its contents? Firefox and Safari differ here. <https://github.com/privacycg/storage-access/issues/2>
When do we age out entries in the global storage access map? See also Scope of Storage Access. <https://github.com/privacycg/storage-access/issues/5>
Shouldn’t step 7 be same site?
Shouldn’t step 3.7 be same site?
Remove step 3.9 if we determine that nested iframes should be able to request storage access. <https://github.com/privacycg/storage-access/issues/10>
since this is UA-defined, does it make sense to follow-up separately with a user prompt?
What this section should look like ultimately hinges on <https://github.com/privacycg/storage-access/issues/3>
Write this section. For each kind of client-side storage affected, modify them to invoke determine if a site has storage access & modify their behavior based on the result.
Should this API affect client-side storage other than cookies? <https://github.com/privacycg/storage-access/issues/4>
Write this section.
What about Feature Policy? <https://github.com/privacycg/storage-access/issues/12>
Write this section.
Write this section.