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:
requestStorageAccess()
, whichrelies on havingcan request access toclient-side storage can work in such User Agents.unpartitioned data from within an iframe.requestStorageAccessForOrigin(origin)
, which allows top-level browsing contexts to request access to unpartitioned data on behalf of another origin .
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:
-
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:
-
Let settings be doc ’s relevant settings object .
-
Let site be the result of obtaining a site from settings ’ origin .
-
If doc ’s browsing context is a top-level browsing context , return the partitioned storage key ( site , site ).
-
Let top-level site be the result of obtaining a site from settings ’ top-level origin .
-
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:
Let settings be doc ’s relevant settings object .
Let top-level site be the result of obtaining a site from settings ’ top-level origin .
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:
-
If map [ key ] does not exist , run these steps:
-
Let flags be a new storage access flag set .
-
Set map [ key ] to flags .
-
-
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:
-
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:
-
Let p be a new promise .
-
If doc ’s origin is an opaque origin , resolve p with false and return p .
-
If doc ’s browsing context is a top-level browsing context , resolve p with true and return p .
-
If the top-level origin of doc ’s relevant settings object is an opaque origin , resolve p with false and return p .
-
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 .
-
Let key be the result of generating a partitioned storage key from doc .
-
If key is failure, resolve p with false and return p .
-
Let global be doc ’s relevant global object .
-
Run these steps in parallel :
-
Let map be the result of obtaining the storage access map for doc .
-
Let flag set be the result of obtaining the storage access flag set with key from map .
-
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.
-
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.
-
Let hasAccess be a new promise .
-
Determine the storage access policy with key , doc and hasAccess .
-
Queue a global task on the permission task source given global to resolve p with the result of hasAccess .
-
-
Return p .
Shouldn’t step 7 be same site ?
When
invoked
on
Document
doc
,
the
requestStorageAccess()
method
must
run
these
steps:
-
Let p be a new promise .
-
If this algorithm was invoked when doc ’s
Window
object did not have transient activation , reject and return p . -
If doc ’s browsing context is a top-level browsing context , resolve and return p .
-
If doc is not allowed to use the
"storage-access"
permission, reject and return p . -
If the top-level origin of doc ’s relevant settings object is an opaque origin , reject and return p .
-
If doc ’s origin is same origin with the top-level origin of doc ’s relevant settings object , resolve and return p .
-
If doc ’s origin is an opaque origin , reject and return p .
-
If doc ’s active sandboxing flag set has its sandbox storage access by user activation flag set, reject and return p .
-
Let key be the result of generating a partitioned storage key from doc .
-
If key is failure, reject and return p .
-
Let global be doc ’s relevant global object .
-
Let map be the result of obtaining the storage access map for doc .
-
Let flag set be the result of obtaining the storage access flag set with key from map .
-
If flag set ’s was expressly denied storage access flag is set, reject and return p .
-
If flag set ’s has storage access flag is set, resolve and return p .
-
Otherwise, run these steps in parallel :
-
Let hasAccess be a new promise .
-
Determine the storage access policy with key , doc and hasAccess .
-
Queue a global task on the permission task source given global to
-
Set flag set ’s has storage access flag .
-
Resolve or reject p based on the result of hasAccess .
-
-
Save the storage access flag set for key in map .
-
-
Return p .
Shouldn’t step 3.7 be same site ?
When
invoked
on
Document
doc
,
the
requestStorageAccessForOrigin(origin)
method
must
run
these
steps:
Let p be a new promise .
If doc is not fully active , then reject p with an "
InvalidStateError
"DOMException
and return p .If this algorithm was invoked when doc ’s
Window
object did not have transient activation , reject and return p .If doc ’s browsing context is not a top-level browsing context , reject and return p .
If doc ’s origin is an opaque origin , reject and return p .
Let parsedURL be the the result of running the URL parser on site .
If parsedURL is failure, reject p with a "
TypeError
"DOMException
and return p .Let origin be parsedURL ’s origin .
If origin is an opaque origin , reject and return p .
If doc ’s origin is same origin with origin , resolve and return p .
Let key be the result of generating a delegated partitioned storage key from doc and origin .
If key is failure, reject and return p .
Let global be doc ’s relevant global object .
Let map be the result of obtaining the storage access map for doc .
Let flag set be the result of obtaining the storage access flag set with key from map .
If flag set ’s was expressly denied storage access flag is set, reject and return p .
If flag set ’s has storage access flag is set, resolve and return p .
Otherwise, run these steps in parallel :
Let hasAccess be a new promise .
Determine the storage access policy with key , doc and hasAccess .
Queue a global task on the permission task source given global to
Set flag set ’s has storage access flag .
Resolve or reject p based on the result of hasAccess .
Save the storage access flag set for key in map .
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:
-
Let map be the result of obtaining the storage access map for doc .
-
Let flag set be the result of obtaining the storage access flag set with key from map .
-
If flag set ’s has storage access flag is set, return true.
-
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 .
-
If has storage access is true, set flag set ’s has storage access flag .
-
Save the storage access flag set for key in map .
-
Return has storage access .
To
determine
the
storage
access
policy
for
partitioned
storage
key
key
with
Document
doc
and
Promise
p
,
run
these
steps:
-
Let map be the result of obtaining the storage access map for doc .
-
Let flag set be the result of obtaining the storage access flag set with key from map .
-
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.
-
Let global be doc ’s relevant global object .
-
If implicitly granted is true, queue a global task on the permission task source given global to resolve p , and return.
-
If implicitly denied is true, queue a global task on the permission task source given global to reject p , and return.
-
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.)
-
If expressly granted is true, run these steps:
-
Unset flag set ’s was expressly denied storage access flag .
-
Save the storage access flag set for key in map .
-
Queue a global task on the permission task source given global to resolve p , and return.
-
-
Unset flag set ’s has storage access flag .
-
If expressly denied is true, run these steps:
-
If doc ’s
Window
object has transient activation , consume user activation with it. -
Set flag set ’s was expressly denied storage access flag .
-
-
Save the storage access flag set for key in map .
-
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?
3.3. Changes to navigation
Before changing the current entry of a session history , run the following steps:
-
Let doc be current entry 's
Document
. -
Let map be the result of obtaining the storage access map for doc ’s browsing context 's top-level browsing context .
-
Let key be the result of generating a partitioned storage key from doc .
-
If key is failure, abort these steps.
-
Let flag set be the result of obtaining the storage access flag set with key from map .
-
Unset flag set ’s has storage access flag .
-
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:
-
The
sandbox
storage
access
by
user
activation
flag
,
unless
tokens
contains
the
allow-storage-access-by-user-activation
keyword.
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

document.
requestStorageAccess()
.
6. Security considerations
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:
-
Let blocked be the result of getting a property from parameters named
blocked
. -
If blocked is not a boolean return a WebDriver error with WebDriver error code invalid argument .
-
Let origin be the result of getting a property from parameters named
origin
. -
If origin is not a single U+002A ASTERISK character (*), then:
-
Let parsedURL be the the result of running the URL parser on origin .
-
If parsedURL is failure, then return a WebDriver error with WebDriver error code invalid argument .
-
Set origin to parsedURL ’s origin .
-
-
If the current browsing context is not a top-level browsing context return a WebDriver error with WebDriver error code unsupported operation .
-
Let doc be the current browsing context 's active document .
-
Let settings be doc ’s relevant settings object .
-
Let top-level site be the result of obtaining a site from settings ’s origin .
-
If origin is a single U+002A ASTERISK character (*), then:
-
If blocked is
true
, then:-
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 .
-
-
Otherwise, if blocked is
false
, then:-
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 .
-
-
-
Otherwise:
-
Let embedded site be the result of obtaining a site from origin .
-
If embedded site is same site with top-level site return a WebDriver error with WebDriver error code unsupported operation .
-
If blocked is
true
, then:-
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 .
-
-
Otherwise, if blocked is
false
, then:-
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 .
-
-
-
If the above implementation-defined step of steps resulted in failure, return a WebDriver error with WebDriver error code unknown error .
-
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 .