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
75 changes: 73 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()}}).
bvandersloot-mozilla marked this conversation as resolved.
Show resolved Hide resolved

<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 calls `{{Document.allowStorageAccessRequestOnSite("https://identity.example")}}` and opens a new [=window=] for `https://identity.example`.
bvandersloot-mozilla marked this conversation as resolved.
Show resolved Hide resolved

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`, the {{Promise}} returned by `{{Document/allowStorageAccessRequestOnSite()}}` has resolved or will resolve shortly. At this point, requests to `https://idnetity.example` will bear the cookie set in the previous [=window=].

Choose a reason for hiding this comment

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

To be clear, is this saying allowStorageAccessRequestOnSite would remaining pending until requestStorageAccessUnderSite is invoked? Note that while authentication flows can happen in popups, they could be done via a full-page redirect.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That was the intent. Given that, I think it would be best to have that Promise resolve once the internal state is set to allow the requestStorageAccessUnderSite call to succeed.

Copy link
Member

Choose a reason for hiding this comment

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

Right, I don't fully understand why the Promise needs to be left dangling on site.example. What was the reason for that?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I wanted a means to communicate the timing of unpartitioned storage availability in the https://site.example page. How can an iframe that is waiting for a separate window to grant it unpartitioned storage know when it has that available? I think the answer right now is polling. Perhaps we want a new function, e.g. waitForStorageAccess() ?

Regardless, I think that this promise should resolve once the intemediate state of "ready for a requestStorageAccessUnderSite call" is set.

Copy link
Member

Choose a reason for hiding this comment

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

Yup, this is #55 and I agree it gets even more interesting with the popup use case.

bvandersloot-mozilla marked this conversation as resolved.
Show resolved Hide resolved

</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,61 @@ 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 |serializedEmbeddingSite| is |"null"| or not a valid [=serialization of a site=], [=reject=] and return |p|.
bvandersloot-mozilla marked this conversation as resolved.
Show resolved Hide resolved
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. Wait for a call to {{Document/requestStorageAccessUnderSite()}} on a {{Document}} with [=site=] |thirdPartySite|.
bvandersloot-mozilla marked this conversation as resolved.
Show resolved Hide resolved
1. Unset |flag set|’s [=has allow request storage access flag=].
1. [=Resolve=] and return |p|.

ISSUE: Vagueness around waiting
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 |serializedEmbeddingSite| is |"null"| or not a valid [=serialization of a site=], [=reject=] and return |p|.
bvandersloot-mozilla marked this conversation as resolved.
Show resolved Hide resolved
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. Notify all waiting tasks of {{Document/allowRequestStorageAccessOnSite()}} with argument corresponding |doc|'s [=site=].
1. [=resolve=] |p|
1. return |p|.
1. If |flag set|’s [=has storage access flag=] is set,
1. Notify all waiting tasks of {{Document/allowRequestStorageAccessOnSite()}} with argument corresponding |doc|'s [=site=].
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. Notify all waiting tasks of {{Document/allowRequestStorageAccessOnSite()}} with argument corresponding |doc|'s [=site=].
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