From 2718adac6f19cc5fd79bd5a8bf1498f6b929246e Mon Sep 17 00:00:00 2001 From: Matt Reichhoff Date: Fri, 19 Aug 2022 13:27:38 -0400 Subject: [PATCH 1/2] Add draft of requestStorageAccessForSite spec --- storage-access.bs | 51 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/storage-access.bs b/storage-access.bs index 79ceba8..0eebbd4 100644 --- a/storage-access.bs +++ b/storage-access.bs @@ -77,7 +77,7 @@ urlPrefix: https://w3c.github.io/webdriver/webdriver-spec.html#; spec: webdriver 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 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 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. It also provides a mechanism to allow top-level sites to request such access on behalf of embedded sites. [[STORAGE-ACCESS-INTRO]] @@ -87,7 +87,7 @@ This specification depends on the Infra standard. [[!INFRA]]

The Storage Access API

-This specification defines a method to query whether or not a {{Document}} currently has access to its [=unpartitioned data=] ({{Document/hasStorageAccess()}}), and a method that can be used to request access to its [=unpartitioned data=] ({{Document/requestStorageAccess()}}). +This specification defines a method to query whether or not a {{Document}} currently has access to its [=unpartitioned data=] ({{Document/hasStorageAccess()}}), a method that can be used to request access to its [=unpartitioned data=] ({{Document/requestStorageAccess()}}), and a method that can be used to request access to [=unpartitioned data=] on behalf of another site ({{Document/requestStorageAccessForSite(site)}}).
@@ -95,7 +95,7 @@ Alex visits `https://social.example/`. The page sets a cookie. This cookie has b 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|`.`{{Document/cookie}}, depending on User Agent storage access policies. -Script in the <{iframe}> can call |doc|`.`{{Document/hasStorageAccess()}} to determine if it has access to the cookie. If it does not have access, it can request access by calling |doc|`.`{{Document/requestStorageAccess()}}. +Script in the <{iframe}> can call |doc|`.`{{Document/hasStorageAccess()}} to determine if it has access to the cookie. If it does not have access, it can request access by calling |doc|`.`{{Document/requestStorageAccess()}}. Alternatively, a script on `https://video.example/` could request access on behalf of `https://social.example` by calling |doc|`.`{{Document/requestStorageAccessForSite(site)}} with {{USVString}} |site| as `https://social.example`.
@@ -139,6 +139,15 @@ To generate a partitioned storage key for a {{Docu 1. Let |top-level site| be the result of [=obtain a site|obtaining a site=] from |settings|' [=top-level origin=]. 1. Return the [=partitioned storage key=] (|top-level site|, |site|). +To generate an overridden partitioned storage key for a {{Document}} |doc| and an [=url/origin=] |override|, run the following steps: + +1. Let |settings| be |doc|'s [=relevant settings object=]. + + +1. Let |top-level site| be the result of [=obtain a site|obtaining a site=] from |settings|' [=top-level origin=]. +1. Let |site override| be the result of [=obtain a site|obtaining a site=] from |override|. +1. Return the [=partitioned storage key=] (|top-level site|, |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 @@ -163,6 +172,7 @@ To save the storage access flag set for a [=partit partial interface Document { Promise<boolean> hasStorageAccess(); Promise<undefined> requestStorageAccess(); + Promise<undefined> requestStorageAccessForSite(USVString site); }; @@ -224,9 +234,41 @@ When invoked on {{Document}} |doc|, the re ISSUE: Shouldn't step 3.7 be [=same site=]? +When invoked on {{Document}} |doc|, the requestStorageAccessForSite(site) 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 |doc|'s [=Document/origin=] is an [=opaque origin=], [=reject=] and return |p|. +1. Let |parsedURL| be the the result of running the [=URL parser=] on |site|. +1. If |parsedURL| is failure, [=reject=] and return |p|. +1. Let |origin| be |parsedURL|'s [=url/origin=]. +1. If |origin| is an [=opaque origin=], [=reject=] and return |p|. +1. If |doc|'s [=Document/origin=] is [=same origin=] with |origin|, [=/resolve=] and return |p|. + + + +1. Let |key| be the result of [=generate an overridden partitioned storage key|generating an overridden partitioned storage key=] from |doc| and |origin|. +1. If |key| is failure, [=reject=] and return |p|. +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, [=reject=] and return |p|. +1. If |flag set|'s [=has storage access flag=] is set, [=/resolve=] and 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 or reject |p| based on the result of |hasAccess|. + 1. [=Save the storage access flag set=] for |key| in |map|. +1. Return |p|. +

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 [[#storage]]) as well as when {{Document/hasStorageAccess()}} and {{Document/requestStorageAccess()}} are called. +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()}}, {{Document/requestStorageAccess()}}, and {{Document/requestStorageAccessForSite(site)}} are called. To determine if a site has storage access with [=partitioned storage key=] |key| and {{Document}} |doc|, run these steps: @@ -268,6 +310,7 @@ ISSUE: [since this is UA-defined, does it make sense to follow-up separately wit Before changing the [=current entry=] of a [=session history=], run the following steps: + 1. Let |doc| be [=current entry=]'s {{Document}}. 1. Let |map| be the result of [=obtain the storage access map|obtaining the storage access map=] for |doc|'s [=Document/browsing context=]'s [=top-level browsing context=]. 1. Let |key| be the result of [=generate a partitioned storage key|generating a partitioned storage key=] from |doc|. From 9c2be33e4958dd1eea2056b5decd3deea74dffc2 Mon Sep 17 00:00:00 2001 From: Matt Reichhoff Date: Wed, 21 Sep 2022 15:06:13 -0400 Subject: [PATCH 2/2] Address johannhof feedback --- storage-access.bs | 47 ++++++++++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/storage-access.bs b/storage-access.bs index 0eebbd4..a6c21aa 100644 --- a/storage-access.bs +++ b/storage-access.bs @@ -77,7 +77,12 @@ urlPrefix: https://w3c.github.io/webdriver/webdriver-spec.html#; spec: webdriver 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 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. It also provides a mechanism to allow top-level sites to request such access on behalf of embedded sites. [[STORAGE-ACCESS-INTRO]] +The Storage Access API enables developers to request access to client-side storage for embedded resources such as iframes, scripts, or images. It does this through two mechanisms: + +1. {{Document/requestStorageAccess()}}, which can request access to unpartitioned data from within an iframe. +1. {{Document/requestStorageAccessForOrigin(origin)}}, which allows top-level browsing contexts to request access to unpartitioned data on behalf of another [=url/origin=]. + +[[STORAGE-ACCESS-INTRO]] @@ -87,7 +92,7 @@ This specification depends on the Infra standard. [[!INFRA]]

The Storage Access API

-This specification defines a method to query whether or not a {{Document}} currently has access to its [=unpartitioned data=] ({{Document/hasStorageAccess()}}), a method that can be used to request access to its [=unpartitioned data=] ({{Document/requestStorageAccess()}}), and a method that can be used to request access to [=unpartitioned data=] on behalf of another site ({{Document/requestStorageAccessForSite(site)}}). +This specification defines a method to query whether or not a {{Document}} currently has access to its [=unpartitioned data=] ({{Document/hasStorageAccess()}}), a method that can be used to request access to its [=unpartitioned data=] ({{Document/requestStorageAccess()}}), and a method that can be used to request access to [=unpartitioned data=] on behalf of another [=url/origin=] ({{Document/requestStorageAccessForOrigin(origin)}}).
@@ -95,7 +100,19 @@ Alex visits `https://social.example/`. The page sets a cookie. This cookie has b 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|`.`{{Document/cookie}}, depending on User Agent storage access policies. -Script in the <{iframe}> can call |doc|`.`{{Document/hasStorageAccess()}} to determine if it has access to the cookie. If it does not have access, it can request access by calling |doc|`.`{{Document/requestStorageAccess()}}. Alternatively, a script on `https://video.example/` could request access on behalf of `https://social.example` by calling |doc|`.`{{Document/requestStorageAccessForSite(site)}} with {{USVString}} |site| as `https://social.example`. +Script in the <{iframe}> can call |doc|`.`{{Document/hasStorageAccess()}} to determine if it has access to the cookie. If it does not have access, it can request access by calling |doc|`.`{{Document/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|`.`{{Document/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|`.`{{Document/requestStorageAccessForOrigin(origin)}} with {{USVString}} |origin| as `https://social.example`.
@@ -139,14 +156,10 @@ To generate a partitioned storage key for a {{Docu 1. Let |top-level site| be the result of [=obtain a site|obtaining a site=] from |settings|' [=top-level origin=]. 1. Return the [=partitioned storage key=] (|top-level site|, |site|). -To generate an overridden partitioned storage key for a {{Document}} |doc| and an [=url/origin=] |override|, run the following steps: - +To generate a delegated partitioned storage key for a {{Document}} |doc| and an [=url/origin=] |override|, run the following steps: 1. Let |settings| be |doc|'s [=relevant settings object=]. - - 1. Let |top-level site| be the result of [=obtain a site|obtaining a site=] from |settings|' [=top-level origin=]. -1. Let |site override| be the result of [=obtain a site|obtaining a site=] from |override|. -1. Return the [=partitioned storage key=] (|top-level site|, |site override|). +1. 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|: @@ -172,7 +185,7 @@ To save the storage access flag set for a [=partit partial interface Document { Promise<boolean> hasStorageAccess(); Promise<undefined> requestStorageAccess(); - Promise<undefined> requestStorageAccessForSite(USVString site); + Promise<undefined> requestStorageAccessForOrigin(USVString origin); }; @@ -234,25 +247,21 @@ When invoked on {{Document}} |doc|, the re ISSUE: Shouldn't step 3.7 be [=same site=]? -When invoked on {{Document}} |doc|, the requestStorageAccessForSite(site) method must run these steps: +When invoked on {{Document}} |doc|, the requestStorageAccessForOrigin(origin) method must run these steps: 1. Let |p| be [=a new promise=]. +1. If |doc| is not [=Document/fully active=], then [=reject=] |p| with an "{{InvalidStateError}}" {{DOMException}} and return |p|. 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 |doc|'s [=Document/origin=] is an [=opaque origin=], [=reject=] and return |p|. 1. Let |parsedURL| be the the result of running the [=URL parser=] on |site|. -1. If |parsedURL| is failure, [=reject=] and return |p|. +1. If |parsedURL| is failure, [=reject=] |p| with a "{{TypeError}}" {{DOMException}} and return |p|. 1. Let |origin| be |parsedURL|'s [=url/origin=]. 1. If |origin| is an [=opaque origin=], [=reject=] and return |p|. 1. If |doc|'s [=Document/origin=] is [=same origin=] with |origin|, [=/resolve=] and return |p|. - - - -1. Let |key| be the result of [=generate an overridden partitioned storage key|generating an overridden partitioned storage key=] from |doc| and |origin|. +1. Let |key| be the result of [=generate a delegated partitioned storage key|generating a delegated partitioned storage key=] from |doc| and |origin|. 1. If |key| is failure, [=reject=] and return |p|. 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, [=reject=] and return |p|. @@ -268,7 +277,7 @@ When invoked on {{Document}} |doc|, the re

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 [[#storage]]) as well as when {{Document/hasStorageAccess()}}, {{Document/requestStorageAccess()}}, and {{Document/requestStorageAccessForSite(site)}} are called. +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()}}, {{Document/requestStorageAccess()}}, and {{Document/requestStorageAccessForOrigin(origin)}} are called. To determine if a site has storage access with [=partitioned storage key=] |key| and {{Document}} |doc|, run these steps: