Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Forward Declared API Addition #100

Closed
70 changes: 68 additions & 2 deletions storage-access.bs
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,9 @@ urlPrefix: https://w3c.github.io/webdriver/webdriver-spec.html#; spec: webdriver

<em>This section is non-normative.</em>

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.
User Agents sometimes prevent third-party content 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.
bvandersloot-mozilla marked this conversation as resolved.
Show resolved Hide resolved

The Storage Access API enables content inside <{iframe}>s 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]]
The Storage Access API enables third parties 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]]

</section>

Expand All @@ -98,6 +98,18 @@ Script in the <{iframe}> can call |doc|`.`{{Document/hasStorageAccess()}} to det

</div>

This specification also defines a method for a site to request access to its [=unpartitioned data=] when later loaded in a [=third party context=] on another specified site ({{Document/requestStorageAccessUnderSite()}}), and a method for a site to allow such requests to succeed ({{Document/allowStorageAccessRequestOnSite()}}).

<div class=example>

Alex visits `https://site.example`. Alex clicks "Login with Identity", which Alex expects to log them in with their account on `https://identity.example`. The page, `https://site.example`, calls `{{Document.allowStorageAccessRequestOnSite("https://identity.example")}}` and opens a new [=window=] for `https://identity.example` when the returned promise resolves.

In the new [=window=], Alex selects the account to log in with. In response, `https://identity.example` sets a cookie, which by virtue of its [=first-party-site context=] is in its [=unpartitioned data=] and calls `{{Document.requestStorageAccessUnderSite("https://site.example")}}`. Once the {{Promise}} returned by `{{Document/requestStorageAccessUnderSite()}}` resolves, the `https://identity.example` [=window=] closes itself, returning Alex to `https://site.example`.

When Alex returns to `https://site.example` requests to `https://identity.example` will bear the cookie set in the previous [=window=].

</div>

<dfn>Unpartitioned data</dfn> 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 <dfn>first-party-site context</dfn> 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 [=environment settings object/origin=] and [=top-level origin=] of its [=relevant settings object=] are [=same site=] with one another.
Expand Down Expand Up @@ -144,6 +156,8 @@ A <dfn>storage access flag set</dfn> is a set of zero or more of the following f
:: 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 <dfn for="storage access flag set" id=was-expressly-denied-storage-access-flag>was expressly denied storage access flag</dfn>
:: 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|.
: The <dfn for="has allow request storage access flag set" id=has-allow-request-storage-access-flag>has allow request storage access flag</dfn>
:: When set, this flag indicates |top-level site| is allowing |embedded site| to request access to its [=unpartitioned data=] on |top-level site| from the |embedded site| [=first-party-site context=].
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder how folks more familiar with spec syntax like @hober or @annevk feel about our usage of "site" in this PR and in SAA overall, and whether it's clear enough what's being referred to. What we really mean is "documents of embedded site" (in a first-party-site context), so maybe we need to introduce this definition separately.


To <dfn type="abstract-op">obtain a storage access flag set</dfn> for a [=partitioned storage key=] |key| from a [=/storage access map=] |map|, run the following steps:

Expand All @@ -162,6 +176,8 @@ To <dfn type="abstract-op">save the storage access flag set</dfn> for a [=partit
partial interface Document {
Promise&lt;boolean> hasStorageAccess();
Promise&lt;undefined> requestStorageAccess();
Promise&lt;undefined> allowStorageAccessRequestOnSite(DOMString serializedSite);
Promise&lt;undefined> requestStorageAccessUnderSite(DOMString serializedSite);
bvandersloot-mozilla marked this conversation as resolved.
Show resolved Hide resolved
};
</pre>

Expand Down Expand Up @@ -223,6 +239,56 @@ When invoked on {{Document}} |doc|, the <dfn export method for=Document><code>re

ISSUE: Shouldn't step 3.7 be [=same site=]?

When invoked on {{Document}} |doc|, the <dfn export method for=Document><code>allowRequestStorageAccessOnSite(DOMString serializedSite)</code></dfn> method must run these steps:

1. Let |p| be [=a new promise=].
1. If this algorithm was invoked when |doc|'s {{Window}} object did not have [=transient activation=], [=reject=] and return |p|.
1. If |doc|'s [=Document/browsing context=] is not a [=top-level browsing context=], [=reject=] and return |p|.
1. If |serializedSite| is |"null"| or not a valid [=serialization of a site=], [=reject=] and return |p|.
1. Let |settings| be |doc|'s [=relevant settings object=].
1. Let |site| be the result of [=obtain a site|obtaining a site=] from |settings|' [=environment settings object/origin=].
1. Let |thirdPartySite| be a [=site=] that serializes to |serializedSite|.
1. Let |key| be the [=partitioned storage key=] (|site|, |thirdPartySite|).
1. Let |global| be |doc|'s [=relevant global object=].
1. Let |map| be the result of [=obtain the storage access map|obtaining the storage access map=] for |doc|.
1. Let |flag set| be the result of [=obtain a storage access flag set|obtaining the storage access flag set=] with |key| from |map|.
1. Set |flag set|’s [=has allow request storage access flag=].
1. [=Save the storage access flag set=] for |key| in |map|.
1. Unset |flag set|’s [=has allow request storage access flag=].
1. [=Resolve=] and return |p|.

ISSUE: Deserialization of site is not well defined

When invoked on {{Document}} |doc|, the <dfn export method for=Document><code>requestStorageAccessUnderSite(DOMString serializedSite)</code></dfn> method must run these steps:

1. Let |p| be [=a new promise=].
1. If this algorithm was invoked when |doc|'s {{Window}} object did not have [=transient activation=], [=reject=] and return |p|.
1. If |doc|'s [=Document/browsing context=] is not a [=top-level browsing context=], [=reject=] and return |p|.
1. If |serializedSite| is |"null"| or not a valid [=serialization of a site=], [=reject=] and return |p|.
1. Let |settings| be |doc|'s [=relevant settings object=].
1. Let |site| be the result of [=obtain a site|obtaining a site=] from |settings|' [=environment settings object/origin=].
1. Let |firstPartySite| be a [=site=] that serializes to |serializedSite|.
1. Let |key| be the [=partitioned storage key=] (|firstPartySite|, |site|).
1. Let |global| be |doc|'s [=relevant global object=].
1. Let |map| be the result of [=obtain the storage access map|obtaining the storage access map=] for |doc|.
1. Let |flag set| be the result of [=obtain a storage access flag set|obtaining the storage access flag set=] with |key| from |map|.
1. If |flag set|’s [=was expressly denied storage access flag=] is set,
1. [=resolve=] |p|
1. return |p|.
1. If |flag set|’s [=has storage access flag=] is set,
1. [=resolve=] |p|
1. return |p|.
1. Otherwise, run these steps [=in parallel=]:
1. Let |hasAccess| be [=a new promise=].
1. [=Determine the storage access policy=] with |key|, |doc| and |hasAccess|.
1. [=Queue a global task=] on the [=permission task source=] given |global| to
1. Set |flag set|'s [=has storage access flag=].
1. Resolve |p|.
1. [=Save the storage access flag set=] for |key| in |map|.
1. Return |p|.



<h4 id="ua-policy">User Agent storage access policies</h4>

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 [[#storage]]) as well as when {{Document/hasStorageAccess()}} and {{Document/requestStorageAccess()}} are called.
Expand Down