The Storage Access API

Draft Community Group Report, 19 August

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

This spec is not yet ready for implementation. It exists in this repository to record the ideas and promote discussion.

Before attempting to implement this spec, please contact the editors.


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 iframe s 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 iframe s developers to request and be granted access to their client-side storage, so that storage for embedded content resources such as iframes, scripts, or images. It does this through two mechanisms:

  1. requestStorageAccess() , which relies on having can request access to client-side storage can work in such User Agents. unpartitioned data from within an iframe.

  2. requestStorageAccessForOrigin(origin) , which allows top-level browsing contexts to request access to unpartitioned data on behalf of another origin .

[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() ), and a method that can be used to request access to unpartitioned data on behalf of another origin ( requestStorageAccessForOrigin(origin) ).

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() .

The API is not limited to iframes. An alternative scenario is:

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 . requestStorageAccessForOrigin(origin) with USVString origin as https://social.example .

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 .

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. [Issue #privacycg/storage-access#2]

When do we age out entries in the global storage access map ? See also Scope of Storage Access . [Issue #privacycg/storage-access#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 settings origin .

  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 settings top-level origin .

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

To generate a delegated partitioned storage key for a Document doc and an origin override , run the following steps:

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

  2. Let top-level site be the result of obtaining a site from settings top-level origin .

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

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();
  Promise<undefined> requestStorageAccessForOrigin(USVString origin);
};

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

  1. Let p be a new promise .

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

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

  4. If the top-level origin of doc ’s relevant settings object is an opaque origin , resolve p with false and return p .

  5. If doc ’s origin is same origin with the top-level origin of doc ’s relevant settings object , resolve p with true and return p .

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

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

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

  9. Run these steps in parallel :

    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 was expressly denied storage access flag is set, queue a global task on the permission task source given global to resolve p with false, and abort these steps.

    4. If flag set ’s has storage access flag is set, queue a global task on the permission task source given global to resolve p with true, and abort these steps.

    5. Let hasAccess be a new promise .

    6. Determine the storage access policy with key , doc and hasAccess .

    7. Queue a global task on the permission task source given global to resolve p with the result of hasAccess .

  10. 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. If this algorithm was invoked when doc ’s Window object did not have transient activation , reject and return p .

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

  4. If doc is not allowed to use the "storage-access" permission, reject and return p .

  5. If the top-level origin of doc ’s relevant settings object is an opaque origin , reject and return p .

  6. If doc ’s origin is same origin with the top-level origin of doc ’s relevant settings object , resolve and return p .

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

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

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

  10. If key is failure, reject and return p .

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

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

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

  14. If flag set ’s was expressly denied storage access flag is set, reject and return p .

  15. If flag set ’s has storage access flag is set, resolve and return p .

  16. Otherwise, run these steps in parallel :

    1. Let hasAccess be a new promise .

    2. Determine the storage access policy with key , doc and hasAccess .

    3. Queue a global task on the permission task source given global to

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

      2. Resolve or reject p based on the result of hasAccess .

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

  17. Return p .

Shouldn’t step 3.7 be same site ?

When invoked on Document doc , the requestStorageAccessForOrigin(origin) 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 this algorithm was invoked when doc ’s Window object did not have transient activation , reject and return p .

  4. If doc ’s browsing context is not a top-level browsing context , reject and return p .

  5. If doc ’s origin is an opaque origin , reject and return p .

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

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

  8. Let origin be parsedURL ’s origin .

  9. If origin is an opaque origin , reject and return p .

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

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

  12. If key is failure, reject and return p .

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

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

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

  16. If flag set ’s was expressly denied storage access flag is set, reject and return p .

  17. If flag set ’s has storage access flag is set, resolve and return p .

  18. Otherwise, run these steps in parallel :

    1. Let hasAccess be a new promise .

    2. Determine the storage access policy with key , doc and hasAccess .

    3. Queue a global task on the permission task source given global to

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

      2. Resolve or reject p based on the result of hasAccess .

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

  19. Return p .

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() , and requestStorageAccessForOrigin(origin) 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. Let global be doc ’s relevant global object .

  5. If implicitly granted is true, queue a global task on the permission task source given global to resolve p , and return.

  6. If implicitly denied is true, queue a global task on the permission task source given global to reject p , and return.

  7. 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.)

  8. 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. Queue a global task on the permission task source given global to resolve p , and return.

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

  10. 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 .

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

  12. Queue a global task on the permission task source given global to reject p .

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 [Issue #privacycg/storage-access#3]

3.4. Changes to various client-side storage mechanisms

Should this API affect client-side storage other than cookies? [Issue #privacycg/storage-access#4]

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. [Issue #privacycg/storage-access#31]

3.4.1. Cookies

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:

4. Permissions Policy Integration

The Storage Access API defines a policy-controlled feature identified by the string "storage-access" . Its default allowlist is "*" .

Note: A Document ’s permissions policy determines whether any content in that document is allowed to request storage access using requestStorageAccess() . If disabled in any document, calling requestStorageAccess() in that document will reject.

5. 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() .

6. Security considerations

Write this section.

7. Automation

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

7.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, 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

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

[DOM]
Anne van Kesteren. DOM Standard . Living Standard. URL: https://dom.spec.whatwg.org/
[ECMASCRIPT]
ECMAScript Language Specification . URL: https://tc39.es/ecma262/multipage/
[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/
[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
[URL]
Anne van Kesteren. URL Standard . Living Standard. URL: https://url.spec.whatwg.org/
[WebDriver]
Simon Stewart; David Burns. WebDriver . URL: https://w3c.github.io/webdriver/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL Standard . Living Standard. URL: https://webidl.spec.whatwg.org/

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();
  Promise<undefined> requestStorageAccessForOrigin(USVString origin);
};

Issues Index

What is the lifecycle of the global storage access map ? How long do we remember its contents? Firefox and Safari differ here. [Issue #privacycg/storage-access#2]
When do we age out entries in the global storage access map ? See also Scope of Storage Access . [Issue #privacycg/storage-access#5]
Shouldn’t step 7 be same site ?
Shouldn’t step 3.7 be same site ?
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 [Issue #privacycg/storage-access#3]
Should this API affect client-side storage other than cookies? [Issue #privacycg/storage-access#4]
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. [Issue #privacycg/storage-access#31]
Write this section.
Write this section.