├── .gitignore
├── images
└── storage-access-prompt.png
├── .pr-preview.json
├── w3c.json
├── .github
└── workflows
│ └── deploy.yml
├── PULL_REQUEST_TEMPLATE.md
├── Makefile
├── explainers
├── fedcm-saa-privacy-questionnaire.md
└── storage-access-for-fedcm.md
├── tag-security-questionnaire.md
├── README.md
└── storage-access.bs
/.gitignore:
--------------------------------------------------------------------------------
1 | build
2 |
--------------------------------------------------------------------------------
/images/storage-access-prompt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/privacycg/storage-access/HEAD/images/storage-access-prompt.png
--------------------------------------------------------------------------------
/.pr-preview.json:
--------------------------------------------------------------------------------
1 | {
2 | "src_file": "storage-access.bs",
3 | "type": "bikeshed",
4 | "params": {
5 | "force": 1
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/w3c.json:
--------------------------------------------------------------------------------
1 | {
2 | "group": 120428,
3 | "contacts": ["hober", "TanviHacks", "erik-anderson", "wseltzer", "weiler"],
4 | "repo-type": "cg-report"
5 | }
6 |
--------------------------------------------------------------------------------
/.github/workflows/deploy.yml:
--------------------------------------------------------------------------------
1 | name: deploy
2 |
3 | on:
4 | push:
5 | branches: [ main ]
6 |
7 | jobs:
8 | build:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - uses: actions/checkout@v3
12 | - uses: actions/setup-python@v4
13 | with:
14 | python-version: '3.10'
15 | - run: pip install bikeshed
16 | - run: bikeshed update
17 | - run: make
18 | - uses: peaceiris/actions-gh-pages@v3
19 | with:
20 | github_token: ${{ secrets.GITHUB_TOKEN }}
21 | publish_dir: build
22 |
--------------------------------------------------------------------------------
/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
4 |
5 | - [ ] At least two implementers are interested (and none opposed):
6 | * …
7 | * …
8 | - [ ] [Tests](https://github.com/web-platform-tests/wpt) are written and can be reviewed and commented upon at:
9 | * …
10 | - [ ] [Implementation bugs](https://github.com/whatwg/meta/blob/main/MAINTAINERS.md#handling-pull-requests) are filed:
11 | * Chromium: …
12 | * Gecko: …
13 | * WebKit: …
14 |
15 | (See [WHATWG Working Mode: Changes](https://whatwg.org/working-mode#changes) for more details.)
16 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | # This Makefile assumes you have a local install of bikeshed. Like any
2 | # other Python tool, you install it with pip:
3 | #
4 | # python3 -m pip install bikeshed && bikeshed update
5 |
6 | # It also assumes you have doctoc installed. This is a tool that
7 | # automatically generates Table of Contents for Markdown files. It can
8 | # be installed like any other NPM module:
9 | #
10 | # npm install -g doctoc
11 |
12 | .PHONY: all publish clean update-explainer-toc
13 | .SUFFIXES: .bs .html
14 |
15 | publish: build/index.html build/images/storage-access-prompt.png
16 |
17 | all: publish update-explainer-toc
18 |
19 | clean:
20 | rm -rf build *~
21 |
22 | update-explainer-toc: README.md Makefile
23 | doctoc $< --title "## Table of Contents" > /dev/null
24 |
25 | build/index.html: storage-access.bs Makefile
26 | mkdir -p build
27 | bikeshed --die-on=warning spec $< $@
28 |
29 | build/images/storage-access-prompt.png: images/storage-access-prompt.png Makefile
30 | mkdir -p build/images
31 | cp images/storage-access-prompt.png build/images
32 |
--------------------------------------------------------------------------------
/explainers/fedcm-saa-privacy-questionnaire.md:
--------------------------------------------------------------------------------
1 | # Security / Privacy Questionnaire for FedCM as a trust signal for the Storage Access API
2 |
3 | > 01. What information does this feature expose,
4 | > and for what purposes?
5 |
6 | This feature exposes no additional information to websites. It allows successful grants of SAA calls when a prior FedCM permission has been allowed by the user. Both APIs allow for cross-site identification, but are gated on user permission.
7 |
8 | > 2. Do features in your specification expose the minimum amount of information
9 | > necessary to implement the intended functionality?
10 |
11 | Yes, no additional information is exposed.
12 |
13 | > 5. Do the features in your specification expose personal information,
14 | > personally-identifiable information (PII), or information derived from
15 | > either?
16 |
17 | Not in itself, though again this feature integrates two existing APIs that are frequently used to transmit such information across sites (gated on user permission).
18 |
19 | > 7. How do the features in your specification deal with sensitive information?
20 |
21 | It doesn't in itself.
22 |
23 | > 8. Does data exposed by your specification carry related but distinct
24 | > information that may not be obvious to users?
25 |
26 | No
27 |
28 | > 10. Do the features in your specification introduce state
29 | > that persists across browsing sessions?
30 |
31 | No, it uses existing permission state from FedCM grants.
32 |
33 | > 12. Do the features in your specification expose information about the
34 | > underlying platform to origins?
35 |
36 | No
37 |
38 | > 14. Does this specification allow an origin to send data to the underlying
39 | > platform?
40 |
41 | No
42 |
43 | > 16. Do features in this specification enable access to device sensors?
44 |
45 | No
46 |
47 | > 17. Do features in this specification enable new script execution/loading
48 | > mechanisms?
49 |
50 | No
51 |
52 | > 18. Do features in this specification allow an origin to access other devices?
53 |
54 | No
55 |
56 | > 19. Do features in this specification allow an origin some measure of control over
57 | > a user agent's native UI?
58 |
59 | No
60 |
61 | > 20. What temporary identifiers do the features in this specification create or
62 | > expose to the web?
63 |
64 | None it itself.
65 |
66 | > 21. How does this specification distinguish between behavior in first-party and
67 | > third-party contexts?
68 |
69 | It follows the existing behavior of SAA and FedCM in 1P / 3P contexts
70 |
71 | > 22. How do the features in this specification work in the context of a browser’s
72 | > Private Browsing or Incognito mode?
73 |
74 | See https://github.com/privacycg/storage-access/blob/main/tag-security-questionnaire.md for how SAA handles private / incognito mode.
75 |
76 | > 24. Does this specification have both "Security Considerations" and "Privacy
77 | > Considerations" sections?
78 |
79 | Yes
80 |
81 | > 26. Do features in your specification enable origins to downgrade default
82 | > security protections?
83 |
84 | Not beyond how SAA already allows for downgrading the security protections afforded by third-party cookie blocking.
85 |
86 | > 28. What happens when a document that uses your feature is kept alive in BFCache
87 | > (instead of getting destroyed) after navigation, and potentially gets reused
88 | > on future navigations back to the document?
89 |
90 | This feature uses long-lived FedCM grants and as such is intended to be usable in future documents or future navigations to the same document.
91 |
92 | > 30. What happens when a document that uses your feature gets disconnected?
93 |
94 | This feature simply adds an additional trust parameter for allowing SAA grants, so this shouldn't be a consideration.
95 |
96 | > 32. Does your feature allow sites to learn about the users use of assistive technology?
97 |
98 | No
99 |
100 | > 34. What should this questionnaire have asked?
101 |
--------------------------------------------------------------------------------
/tag-security-questionnaire.md:
--------------------------------------------------------------------------------
1 | 01. What information might this feature expose to Web sites or other parties,
2 | and for what purposes is that exposure necessary?
3 |
4 | The Storage Access API enables the removal of cross-site cookies. Specifically, it allows the authenticated embeds use case to continue to work. As such, the API provides a way for developers to re-gain access to cross-site cookies, albeit under further constraints.
5 |
6 | A nested Document gains access to the same cookies it has as the active document of a top-level browsing context when it calls `document.requestStorageAccess()` and is returned a resolving Promise (however, `SameSite` restrictions will still apply, i.e., `SameSite=Lax` or `Strict` cookies will not be available, which is important for security but doesn't affect the privacy considerations).
7 | With these cookies it can authenticate itself to the server and load user-specific information.
8 |
9 | While this functionality comes with a risk of abuse by third parties for tracking purposes, it is an explicit goal of the API and a key to its design to not undermine the gains of cross-site cookie deprecation.
10 |
11 | 02. Do features in your specification expose the minimum amount of information
12 | necessary to enable their intended uses?
13 |
14 | Where possible, yes:
15 |
16 | - Permission grants for storage access are double-keyed on a (site, site) basis, meaning that requesting documents will not be able to access cross-site cookies outside of the top-level site that they were granted access under.
17 |
18 | - To a single requesting document, this feature allows access to all its cross-site cookies (if given permission), which may or may not exceed the minimum necessary amount of information that document needs. However, given the flexible nature of cookies it is very hard to determine which cookies a document needs and developer flexibility is an explicit goal of the API. Also, from a privacy perspective, passing any single high-entropy identifier such as a cookie across the site boundary is equivalent to full cross-site cookie access.
19 |
20 | - For security reasons, this API applies a few restrictions to how much information is exposed to a site that is granted storage access:
21 | - Each storage access grant applies only to the nested document that requested it. Other documents have to re-request access, to ensure explicit opt-in from endpoints and prevent attacks from embedders.
22 | - This specification calls out that implementers should still follow SameSite rules when attaching cross-site cookies with storage access. This API does not intend to waive existing security protections.
23 |
24 | 03. How do the features in your specification deal with personal information,
25 | personally-identifiable information (PII), or information derived from
26 | them?
27 |
28 | As mentioned, the SAA enables sharing of information through cross-site cookies, but does not expand on that or deal directly with PII in any way.
29 |
30 | 04. How do the features in your specification deal with sensitive information?
31 |
32 | See above.
33 |
34 | 05. Do the features in your specification introduce new state for an origin
35 | that persists across browsing sessions?
36 |
37 | Yes, a new "storage-access" permission that is managed via the permissions API and is double-keyed on (site, site). This should make it impossible for sites to access the new state across different top-level contexts.
38 |
39 | 06. Do the features in your specification expose information about the
40 | underlying platform to origins?
41 |
42 | No
43 |
44 | 07. Does this specification allow an origin to send data to the underlying
45 | platform?
46 |
47 | No
48 |
49 | 08. Do features in this specification enable access to device sensors?
50 |
51 | No
52 |
53 | 09. Do features in this specification enable new script execution/loading
54 | mechanisms?
55 |
56 | No
57 |
58 | 10. Do features in this specification allow an origin to access other devices?
59 |
60 | No
61 |
62 | 11. Do features in this specification allow an origin some measure of control over
63 | a user agent's native UI?
64 |
65 | While showing UI for storage access prompts is left largely implementation-defined, this API can generally be expected to enable origins to spawn permission prompts detailing the top-level site and the embedded site in the UI.
66 |
67 | We have added a number of anti-abuse, spam and annoyance protections as outlined in the security considerations of the spec.
68 |
69 | 12. What temporary identifiers do the features in this specification create or
70 | expose to the web?
71 |
72 | None
73 |
74 | 13. How does this specification distinguish between behavior in first-party and
75 | third-party contexts?
76 |
77 | This specification is meant to be used in third-party contexts, i.e. in cross-site contexts (see https://tess.oconnor.cx/2020/10/parties). While the JS API itself will be accessible in a same-site context, it will resolve successfully by default without setting a storage access permission, to inform the developer that storage should be accessible by virtue of being in a same-site context. As such, the storage-access permission is not meaningful for any storage restrictions in same-site contexts (whatever those may look like).
78 |
79 | 14. How do the features in this specification work in the context of a browser’s
80 | Private Browsing or Incognito mode?
81 |
82 | The specification currently makes no explicit recommendation, as preferences may differ between user agents. It can generally be expected that in most user agents cross-site cookies are disabled in Private Browsing contexts, which would make it a natural fit for SAA. However, exposing prompts to users in private browsing that request sharing of data between two sites may be viewed as intrusive. The API includes sufficient mechanisms for user agents to always deny storage access requests in private browsing.
83 |
84 | 15. Does this specification have both "Security Considerations" and "Privacy
85 | Considerations" sections?
86 |
87 | Yes
88 |
89 | 16. Do features in your specification enable origins to downgrade default
90 | security protections?
91 |
92 | To some extent, yes, albeit with strong controls to prevent accidental loss of protection. Deprecation of cross-site cookies prevents certain class of attacks as [outlined in detail in this document](https://docs.google.com/document/d/1AsrETl-7XvnZNbG81Zy9BcZfKbqACQYBSrjM3VsIpjY/edit#heading=h.vb3ujl8dnk4q).
93 |
94 | We apply the security restrictions as mentioned in point 2), specifically requiring strict per-document opt-in through calling rSA(), to avoid this loss of security.
95 |
96 | 17. How does your feature handle non-"fully active" documents?
97 |
98 | It will reject calls to rSA and hSA with an exception, as detailed in the spec.
99 |
100 | 18. What should this questionnaire have asked?
101 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # The Storage Access API
2 |
3 | A [Work Item](https://privacycg.github.io/charter.html#work-items) of
4 | the [Privacy Community Group](https://privacycg.github.io/).
5 |
6 | ## Editors:
7 |
8 | - Benjamin VanderSloot
9 | - Johann Hofmann
10 | - Anne van Kesteren
11 |
12 | ## Participate
13 | - https://github.com/privacycg/storage-access/issues
14 |
15 |
16 |
17 | ## Table of Contents
18 |
19 | - [Introduction](#introduction)
20 | - [Motivating Use Cases](#motivating-use-cases)
21 | - [Social Network Commenting Widget](#social-network-commenting-widget)
22 | - [Third-Party Payment Provider](#third-party-payment-provider)
23 | - [Subscribed Video Service](#subscribed-video-service)
24 | - [Non-Goals](#non-goals)
25 | - [The API](#the-api)
26 | - [hasStorageAccess](#hasstorageaccess)
27 | - [requestStorageAccess](#requeststorageaccess)
28 | - [Scope of Storage Access](#scope-of-storage-access)
29 | - [Key scenarios](#key-scenarios)
30 | - [The User Is Not Yet Logged In To the Embedee](#the-user-is-not-yet-logged-in-to-the-embedee)
31 | - [The User Opts Out](#the-user-opts-out)
32 | - [Detailed Design Discussion](#detailed-design-discussion)
33 | - [Recovery Path](#recovery-path)
34 | - [Timeout of an Opt In](#timeout-of-an-opt-in)
35 | - [Compatibility Measure](#compatibility-measure)
36 | - [Considered Alternatives](#considered-alternatives)
37 | - [Automatically Grant Access to Websites Used Often](#automatically-grant-access-to-websites-used-often)
38 | - [Automatically Grant Access Upon User Interaction](#automatically-grant-access-upon-user-interaction)
39 | - [Stakeholder Feedback / Opposition](#stakeholder-feedback--opposition)
40 | - [References & Acknowledgements](#references--acknowledgements)
41 |
42 |
43 |
44 | ## Introduction
45 |
46 | Browsers may block third-party resources from accessing cookies and other storage for privacy and security reasons. The
47 | most popular reason is cross-site tracking prevention. Such blocking breaks authenticated cross-site embeds such as
48 | commenting widgets, embedded payment providers, and subscribed video services.
49 |
50 | The Storage Access API provides a means for authenticated cross-site embeds to check their blocking status and request
51 | access to storage if they are blocked.
52 |
53 | ## Motivating Use Cases
54 |
55 | These use cases assume that the third-party is blocked from cookie access and possibly other storage too.
56 |
57 | ### Social Network Commenting Widget
58 | The user is logged in to Social Network Example with domain name social.example and is now visiting blog.example which
59 | allows readers to comment on blogposts using their social.example account. The user taps/clicks in the commenting
60 | widget, a cross-site iframe from social.example, to make a comment. The onclick event handler in the iframe calls the
61 | Storage Access API to request cookie access needed to authenticate the user cross-site. The user has not commented on
62 | this blog before and thus gets prompted to allow or disallow storage access, decides to allow storage access, and
63 | proceeds to comment on the blogpost.
64 |
65 | ### Third-Party Payment Provider
66 | The user is logged in to Payments Example with domain name payments.example and is now shopping on clothes.example. They
67 | decide to check out and pick Payments Example as their preferred way of paying. The Payments Example option is served
68 | through a cross-site iframe from payments.example that calls the Storage Access API upon the tap/click to pick it as
69 | payment option. The user has used Payments Example previously on this shopping site and thus already been prompted for
70 | storage access. Therefore, the user is not prompted again, the iframe is automatically granted storage access, and
71 | the user proceeds to fulfill the payment.
72 |
73 | ### Subscribed Video Service
74 | The user is logged in to Online Videos Example with domain name videos.example and is paying for an ad-free video
75 | experience. Now they are on games.example and want to watch a video of an exciting game play. The video is served in a
76 | cross-site iframe from videos.example and the iframe calls the Storage Access API upon the tap/click on its play button.
77 | The user has used Online Videos previously on this gaming site and thus is not prompted, the iframe is automatically
78 | granted storage access, and the user proceeds to watch the video.
79 |
80 | ## Non-Goals
81 |
82 | The Storage Access API is not intended to grant arbitrary third-parties cookie access. It is only intended to grant
83 | cookie access to third parties that the user actively uses as first party too, i.e. websites the user recognizes and
84 | uses.
85 |
86 | The Storage Access API can be used for many more things than authenticated embeds, for instance single sign-on,
87 | cross-site subscription services, and federated logins. However, those are not the primary goals of this API and thus,
88 | requirements that are serving those use cases but not the authenticated embed use case might not be met by the
89 | Storage Access API. That said, the Storage Access API is not in conflict with single sign-on, cross-site subscription
90 | services, and federated logins.
91 |
92 | The Storage Access API is not a gateway into a legacy or quirks mode with which third-parties request permission to
93 | get back to a state similar to before e.g. tracking prevention features. Concretely, granted storage access should
94 | not be interpreted as an "allow cross-site tracking mode" or "make old things work mode." The API is opt-in and is
95 | intended for modern scenarios that are created under the assumption of no third-party storage access by default.
96 |
97 | ## The API
98 |
99 | The Storage Access API lives under the document object since it controls document.cookie and the scope of the storage
100 | access may be tied to the scope of the document.
101 |
102 | ### hasStorageAccess
103 |
104 | ```js
105 | var promise = document.hasStorageAccess();
106 | promise.then(
107 | function (hasAccess) {
108 | // Boolean hasAccess says whether the document has access or not.
109 | },
110 | function (reason) {
111 | // Promise was rejected for some reason.
112 | }
113 | );
114 | ```
115 |
116 | ### requestStorageAccess
117 |
118 | ```html
119 |
132 |
133 | ```
134 |
135 | ### Scope of Storage Access
136 |
137 | If an iframe is granted storage access through the API, only that calling iframe and its subresources should have
138 | access to storage.
139 |
140 | The length in time of storage access is up to the browser. It could be for:
141 | - The lifetime of the frame as long as it's hosting content from the same website.
142 | - The lifetime of the top page.
143 | - The lifetime of the browsing session.
144 | - A certain amount of calendar time such as seven days.
145 |
146 | It should be noted that since only the calling iframe gets storage access, it's hard to grant storage access across
147 | browsing sessions or page loads.
148 |
149 | ## Key scenarios
150 |
151 | ### The User Is Not Yet Logged In To the Embedee
152 |
153 | In the case of the user not being logged in to the embeddee, there it should be possible for the iframe to make use of
154 | the user gesture that was required to call document.requestStorageAccess() to also do a popup to enable the user to log
155 | in.
156 |
157 | ### The User Opts Out
158 |
159 | In the case of the user being prompted for storage access and explicitly opts out, the requesting iframe should not be
160 | able to prompt again or do a popup without receiving another user gesture.
161 |
162 | ## Detailed Design Discussion
163 |
164 | ### Recovery Path
165 |
166 | If the user explicitly opts out when prompted for storage access, they may find themselves in a situation they don't
167 | like or didn't expect, such as no ability to comment on a blogpost or ads in a video feed from a service they pay for to
168 | be ad-free. In short, there needs to be a recovery path.
169 |
170 | One way of dealing with this is to allow at least two prompts per embed. If the user explicitly opts out twice, it's a
171 | done deal.
172 |
173 | Another way of dealing with this is to offer affordances to reset choices in browser settings.
174 |
175 | ### Timeout of an Opt In
176 |
177 | If the user explicitly grants storage access to an embedee, the question is for how long that grant should last before a
178 | new prompt is shown? Options include 1) as long as the user keeps re-engaging with the embedee on an
179 | hourly/daily/weekly/monthly basis, 2) with a static timeout of e.g. 30 days, or 3) only for the lifetime of the embedded
180 | document.
181 |
182 | ### Compatibility Measure
183 |
184 | A compatibility measure that has proven to be effective in the wild (shipping in Safari and Firefox) is to automatically
185 | open up page-wide storage access for a third-party that opens a popup through ```window.open()``` and receives user
186 | interaction in that popup.
187 |
188 | This page-wide scope is a good example of a legacy mode of sorts which is not the intention of the Storage Access API,
189 | as explained in [Non-Goals](#non-goals).
190 |
191 | ## Considered Alternatives
192 |
193 | There are some possible alternatives.
194 |
195 | ### Automatically Grant Access to Websites Used Often
196 |
197 | Instead of requiring an explicit API call, the browser could keep track of which websites the user engages with a lot,
198 | possibly even know which websites the user is logged in to, and grant storage access to those websites automatically.
199 |
200 | However, it may be undesirable to allow global cross-site authentication by a third party based on activity on a
201 | first-party website. In fact, such blanket cross-site authentication/identification is often what blocking of
202 | third-party cookies and storage is trying to avoid.
203 |
204 | ### Automatically Grant Access Upon User Interaction
205 |
206 | Instead of requiring an explicit API call, the browser could grant storage access to embedded third-party iframes upon
207 | user interaction with the iframe. This allows for blocking of third-party cookies in all passive scenarios such as
208 | pure page loads and scrolling.
209 |
210 | However, such behavior may incentivize third-parties to render iframes solely for the purposes of getting cookie access,
211 | for instance through invisible overlay iframes à la Clickjacking or through iframes that look like first-party content.
212 |
213 | ## Stakeholder Feedback / Opposition
214 |
215 | - Safari : Shipping
216 | - Firefox : Shipping
217 | - Edge : Implementing
218 | - Brave : Positive
219 | - Chrome : Shipping
220 |
221 | ## References & Acknowledgements
222 |
223 | Several people have provided valuable feedback already in the
224 | [WHATWG HTML issue](https://github.com/whatwg/html/issues/3338) filed on Jan 10, 2018. We're thankful for all that
225 | engagement.
226 |
--------------------------------------------------------------------------------
/explainers/storage-access-for-fedcm.md:
--------------------------------------------------------------------------------
1 | # FedCM as a trust signal for the Storage Access API
2 |
3 | ## Authors
4 |
5 | * [Johann Hofmann](https://github.com/johannhof)
6 | * [Chris Fredrickson](https://github.com/cfredric)
7 | * [Yi Gu](https://github.com/yi-gu)
8 |
9 | ## Table of Contents
10 |
11 | - [Introduction](#introduction)
12 | - [Goals](#goals)
13 | - [Non-goals](#non-goals)
14 | - [Motivation](#motivation)
15 | - [Example Use cases](#example-use-cases)
16 | - [API Details](#api-details)
17 | - [Considered alternatives](#considered-alternatives)
18 | - [Privacy Considerations](#privacy-considerations)
19 | - [Security Considerations](#security-considerations)
20 |
21 | ## Introduction
22 |
23 | When a user consents to using their identity with a 3rd party Identity Provider (IdP) on a Relying Party (RP), many IdPs require third-party cookies to function correctly and securely. Currently, while these IdPs might prefer to use [FedCM](https://github.com/fedidcg/FedCM) for an overall better user experience, they would not be able to for lack of cookie access and would have to show the more generic [Storage Access API](https://github.com/privacycg/storage-access) (SAA) prompts instead.
24 |
25 | This proposal aims to satisfy that requirement in a private and secure manner by updating the Storage Access API (SAA) permission checks to not only accept the permission grant that is given by a storage access prompt, but also the permission grant that is given by a FedCM prompt.
26 |
27 | A key property of this mechanism is limiting the grant to cases explicitly allowed by the RP via the FedCM permissions policy, enforcing a per-frame control for the RP and preventing passive surveillance by the IdP beyond the capabilities that FedCM already grants, as outlined in the [Privacy Considerations](#privacy-considerations).
28 |
29 | Where there are no negative implications for user privacy and security, we propose following the more flexible double-keyed per-site grant scope of SAA vs. the triple-keyed per-origin scope of FedCM as described in the [API Details](#api-details), to maximize developer utility.
30 |
31 | ## Goals
32 |
33 |
34 | * Improve the user experience by enabling cookie-based identity / sign-in use cases to use FedCM.
35 | * Allow developers to avoid explicit token handling and the usage of HttpOnly and Secure attributes via cookies, to reduce susceptibility to cookie theft attacks.
36 | * Securely mediate access to cookies via FedCM through explicit Storage Access opt-in.
37 |
38 |
39 | ## Non-Goals
40 | * Change the privacy and security model behind a FedCM grant. Accepting the prompt should have the same privacy and security implications to a user as before. Specifically, any continued access to cross-site data for an IdP after a FedCM grant should require [active collaboration from an RP](#privacy-considerations).
41 | * Deprecate Storage Access API prompts. This proposal adds an additional means of utilizing the SAA, we do not propose removing other ways of setting storage access permissions, such as the UI shown by browsers as part of a “traditional” storage access request. It’s important to preserve this shared prompt for web interop and we encourage developers with cross-site cookie access needs to fall back to it where possible.
42 |
43 | ## Motivation
44 |
45 | [FedCM](https://fedidcg.github.io/FedCM/) is an API that mediates federated user identity flows through the application of (ideally) well-understood, purpose-driven user interfaces. Using the `navigator.credentials` API, it exposes a high-entropy user identifier (token) from an IdP to an RP.
46 |
47 | This kind of token based authentication/authorization is commonly used in federated identity flows, which FedCM intends to address. Other login schemes, particularly for Single-Sign-On (SSO), rely on the presence of (cross-site) cookies as explained in the examples below.
48 |
49 | These kind of flows can currently be solved by the Storage Access API, which mediates permission for documents to access cross-site cookies. However, as SAA lacks additional context on the nature of the request for cross-site cookie access, its UI in browsers tends to be very generic and unintuitive. It’s also primarily designed to solve use cases for authenticated embeds, which makes it difficult to fit seamlessly onto SSO flows (without sacrificing some of the anti-abuse mechanics of SAA such as the prior user gesture requirement).
50 |
51 | Due to its arguably much more intuitive user experience for mediating user identity, FedCM would be a good fit to cover these login-oriented use cases instead. To do this, it needs to be compatible with identity flows that require cookies to work. This is where a well-designed integration with SAA can help, by providing secure access to cross-site cookies based on FedCM grants as an additional trust signal.
52 |
53 |
54 | ### Example Use Cases
55 |
56 | These are examples we’ve found in our work with partners on resolving breakage from the deprecation of third-party cookies, but it’s important to note that this list does not exhaustively describe the utility a flexible integration such as the one proposed here would bring to developers.
57 |
58 |
59 | #### SSO through SAML in subresources
60 |
61 | Consider dashboard.example, an enterprise application which provides a centralized integration of various custom views from third-party analytics vendors. It does this by displaying a number of cross-site iframes (called embed.example in the below graphic) that contain relevant business information for the user, and as such need to be gated on some user authentication. Authenticating the user to these individual iframes is done seamlessly via an IDP (idp.example), which the user initially logs into in a top-level context.
62 |
63 | 
64 |
65 |
66 | During the initial login flow, the IDP stores the user identity in a SameSite=None cookie and can now function as an SSO provider, commonly via brief redirects to idp.example which returns a SAML response (or similar) to the originally requested site. This works well in a top-level context, where idp.example has access to its first-party cookies, but third-party cookie blocking prevents it from working in embedded contexts.
67 |
68 | This is an issue for dashboard.example, as each of its embeds needs to authenticate through a redirect to idp.example.
69 |
70 | 
71 |
72 | With this proposal, dashboard.example could initially authenticate with idp.example via FedCM. Then, assuming the user grants this request, idp.example would be allowed to use SAA (possibly via [storage access headers](https://github.com/cfredric/storage-access-headers)) to enable its identity flow.
73 |
74 | 
75 |
76 | Switching to a cookie-less flow would have a number of drawbacks for idp.example in this case, notably the loss of cookie-specific security mechanics such as HttpOnly, the departure from established authentication patterns and the need to update third-party software that integrates with their services.
77 |
78 |
79 | #### Iframe-based token refresh
80 |
81 | For access control reasons, some IdPs want to avoid exposing long-lived and/or permissive tokens to its RPs through FedCM credentials. Instead, the IdP maintains an iframe embedded in the RP page, which periodically refreshes tokens and transmits them to the RP for usage as API tokens, encryption keys, etc.
82 |
83 | 
84 |
85 |
86 | However, in order to perform this refresh, the IdP still needs to retain access to some longer lasting authentication token. This is typically done through the use of third-party cookies, which have excellent security properties as they’re accessible to the embedded IdP but not readable by its embedder or any (injected) scripts on the page.
87 |
88 |
89 | ## API Details
90 |
91 | It’s important to note that we are not proposing any new web API surface, but instead a behavior change for the Storage Access API to consider FedCM grants. As such, the code examples might not reveal a lot of information about the desired effect of this proposal.
92 |
93 |
94 | ### Current FedCM / SAA functionality
95 |
96 | Today, using FedCM, RPs can call FedCM to establish a persistent cross-site connection between themselves and specific IDPs.
97 |
98 | ```javascript
99 | // In RP top-level document, where RP and IDP are cross-site:
100 |
101 | // Ensure FedCM permission has been granted.
102 | const cred = await navigator.credentials.get({
103 | identity: {
104 | providers: [{
105 | // normal IDP config, elided here.
106 | }],
107 | },
108 | mediation: 'optional', // default mediation mode
109 | });
110 | ```
111 |
112 | On initial use, this will prompt the user for permission via sign-in prompt mediated by the browser. The RP will use the FedCM API to sign-in the user (if needed), which creates the "connection" between the RP and IDP in the `connected accounts set` internally. This connection is keyed by the RP origin (the top-level origin), the IDP origin, the origin of the embedder when used in an iframe, and the account identifier.
113 |
114 | As a result, cross-site credentials are returned and future invocations of `navigator.credentials.get() `under the same set of keys will not require browser mediation / user permission.
115 |
116 | At this point, many IDPs have a need for access to 3rd party cookies, which, from a privacy perspective, would not exceed the capabilities already granted to them through the ability to exchange high-entropy credentials, but presents greater flexibility, security and a more powerful API.
117 |
118 | To do this at the moment, they would have to invoke `document.requestStorageAccess()` which requires a new user gesture and spawns _another_ user permission prompt (they just accepted the FedCM prompt!)
119 |
120 |
121 | ### SAA Autogrants
122 |
123 | Instead of showing this additional prompt, we propose that `document.requestStorageAccess() `take into consideration the `connected accounts set` that contains information about which RPs and IDPs are allowed to exchange cross-site information and considers it equivalent to the presence of a [storage-access](https://privacycg.github.io/storage-access/#permissiondef-storage-access) permission. When an RP/IDP pair has been approved for access to cross-site credentials, taking into account FedCM concepts such as [prevent silent access](https://w3c.github.io/webappsec-credential-management/#origin-prevent-silent-access-flag), it is also allowed storage access.
124 |
125 | This comes without additional user activation or prior top-level user interaction requirements as it is treated like a granted permission (which seems most accurate given that the user has in fact interacted with a permission prompt from a FedCM IDP).
126 |
127 | **Note that the automatic grant will not lead to a new storage-access permission being created.** FedCM grants work slightly differently (see below) and have different lifetimes, making it hard to convert them directly to storage-access permissions. It’s also undesirable for the user (agent) to maintain two separate permissions that grant a similar capability.
128 |
129 | Instead, the `document.requestStorageAccess()` call would set the environment’s [has storage access](https://privacycg.github.io/storage-access/#environment-has-storage-access) flag to true (i.e. [processing the FedCM grant as a permission state](https://privacycg.github.io/storage-access/#the-document-object:~:text=Let-,process%20permission%20state,-be%20an%20algorithm)), granting the iframe document access to cross-site cookies.
130 |
131 | ```javascript
132 | // In RP top-level document, where RP and IDP are cross-site:
133 |
134 | // Ensure FedCM permission has been granted.
135 | const cred = await navigator.credentials.get({
136 | identity: {
137 | providers: [{
138 | // normal IDP config, elided here.
139 | }],
140 | },
141 | mediation: 'optional',
142 | });
143 |
144 | // In an embedded IDP iframe:
145 |
146 | // No user gesture is needed to call this, and the call will be auto-granted.
147 | await document.requestStorageAccess();
148 | // This returns “true”.
149 | const hasAccess = await document.hasStorageAccess();
150 | ```
151 |
152 | This same check may be performed by proposed SAA extensions such as [requestStorageAccessFor](https://github.com/privacycg/requestStorageAccessFor) and [Storage Access Headers](https://github.com/cfredric/storage-access-headers) to enable additional flexibility for web developers.
153 |
154 |
155 | ### Dealing with scope differences
156 |
157 | One complication to this idea is the fact that FedCM is scoped by origin, while SAA grants are scoped to site. FedCM also [differs in its permission keying](https://fedidcg.github.io/FedCM/#issue-d2fd6198) (which is to be specified). While storage-access permissions are keyed by (top-level, embed), FedCM grants are keyed by (top-level, embedder (RP), IDP).
158 |
159 | This decision makes sense for FedCM as the tokens it mediates are directly returned to the caller of `navigator.credentials.get()`, and as such should be same-origin restricted by default. Additionally, merely double-keying by (RP, IDP) would allow an attacker iframe embedded in rp.example extract information about an adjacent idp.example using FedCM.
160 |
161 | This restriction is not needed for SAA, as the information it mediates comes from cookies, where cross-subdomain sharing is an opt-in feature (via the Domain attribute), or [other storage types](https://github.com/arichiv/saa-non-cookie-storage/) which are already same-origin by default. This means that any same-site cross-origin sibling of the site calling FedCM will only have access to its own storage or explicitly registrable domain-scoped cookies.
162 |
163 | There are still attacks (such as CSRF and click-jacking) and cross-site leaks which try to take advantage of this credentialed state without directly reading it. SAA, through its explicit opt-in mechanisms, puts embed developers at a much lower risk of being exposed to such an attack.
164 |
165 | Although there is ostensibly little privacy or security benefit as shown above, with this proposal, we make it possible for the FedCM and SAA integration to honor the stricter scope of FedCM. Put differently, `document.requestStorageAccess() `auto-grants could be restricted to contexts in which RP and IDP are same-origin to their entries in the `connected accounts set`.
166 |
167 | ```javascript
168 | // In top-level rp.example:
169 |
170 | // Ensure FedCM permission has been granted.
171 | const credential = await navigator.credentials.get({
172 | identity: {
173 | providers: [{
174 | configURL: "https://accounts.idp.example/manifest.json",
175 | clientId: "123",
176 | }]
177 | }
178 | });
179 |
180 | // In an embedded accounts.idp.example iframe, this call will automatically grant.
181 | await document.requestStorageAccess();
182 |
183 |
184 | // In an embedded idp.example iframe, should this automatically grant as well?
185 | await document.requestStorageAccess();
186 | ```
187 |
188 | The result of that choice would be that users could encounter situations where an embedded call to `document.requestStorageAccess()`is not automatically granted despite a FedCM grant being present for a (top-level site, embed site) pair (and the user having seen and accepted the FedCM prompt).
189 |
190 | Overall, there seem to be 3 different options to pursue:
191 |
192 |
193 |
194 | 1. (Preferred) Grant storage-access with a wider scope than FedCM, by extracting the corresponding (top-level site, embedded site) pair from the FedCM grant.
195 |
196 | This gives the greatest amount of developer flexibility and incurs no additional privacy risks (given that (partitioned) cookies can already be site scoped) and little security risk as explained above.
197 |
198 |
199 |
200 | 2. Expose additional parameters for RP and IDP to control the scope of storage access.
201 |
202 | This would require changes in the FedCM API and increase overall complexity for developers. It also wouldn’t effectively protect the origin boundary given that any developer with a need to increase the scope to site could easily opt into it.
203 |
204 |
205 |
206 | 3. Simply match the scope of the FedCM grant and fall back to default SAA processing of a request (usually show a prompt) when it does not match.
207 |
208 | This preserves the original FedCM scope, but could lead to additional SAA prompts showing in some cases. Another drawback is the developer-facing inconsistency of different storage access grants and the need to manage different types of storage access grants.
209 |
210 | In the end, option 3 could prevent developers from building useful cross-site integrations and/or force them to show an SAA prompt with the same sites that the user just consented to via FedCM, which seems like a jarring user experience (or not use FedCM at all). Hence, we recommend option 1.
211 |
212 |
213 | ### Interaction with Permissions Policy
214 |
215 | As explained in a lot of detail in the [Privacy Considerations](#privacy-considerations), an important property of this proposal is that it does not regress on the existing privacy guarantees of the FedCM API. One such guarantee is that the RP stays in control over when an IdP can access cross-site information via tokens through calling navigator.credentials.get(). This happens via the ["identity-credentials-get" Permissions Policy](https://fedidcg.github.io/FedCM/#permissions-policy-integration), which requires an opt-in by the RP.
216 |
217 | To restrict storage access to only succeed when an IdP would be otherwise able to access cross-site credentials via navigator.credentials.get(), we propose that **both** the "identity-credentials-get" permission policy and the "storage-access" permission policy will be considered by the storage access integration.
218 |
219 | This would require iframes which intend to use SAA with FedCM to have the `allow="identity-credentials-get"` attribute and **not** have an `allow="storage-access` none” (or similar) attribute.
220 |
221 | As a side effect, this also gives RPs the ability to explicitly disallow IdPs from using Storage Access, though we haven’t identified a use case for this.
222 |
223 |
224 | ## Considered Alternatives
225 |
226 | * **Have FedCM set a new storage-access permission upon user grant.**
227 | As an alternative approach, upon successful grant, FedCM could simply set a new storage-access permission scoped to the respective (top-level site, embedded site) pair (i.e. RP and IDP). We discarded this idea as it has a few drawbacks:
228 |
229 | * Creating two simultaneous permissions / grants for a similar capability. This seems confusing for the user.
230 | * Relatedly, developers would now have to manage the permission for SAA, including different permission expiry from the FedCM grant.
231 | * This would be necessarily tied to the (broader) SAA permission scope.
232 | * This would be much harder to regulate via the “identity-credentials-get” permissions policy.
233 | * **Modify requestStorageAccess to accept a credential object, and auto-grant SAA permission if the credential contains appropriate metadata.**
234 | This was an initial idea to ensure storage access it strictly tied to the availability of an identity credential. It seems unnecessarily complicated, given that the current proposal is able to enact the same scope without this measure.
235 |
236 | ## Privacy Considerations
237 |
238 | ### RP Control over IDP Storage Access
239 |
240 | For both FedCM and the Storage Access API, their primary consideration for user privacy is the ability to link the user identity between two sites. In the case of FedCM, this happens through sharing a high-entropy identifier, with Storage Access API, shared identity can be established through the re-introduction of cross-site cookies for the embedded 3rd party (the IDP).
241 |
242 | Both APIs are built for this purpose, and as such come with their own mechanisms (in most cases, prompts), to ensure sufficient user consent.
243 |
244 | Does that mean that both exhibit the same risks to the user when one or more parties in the process are participating in cross-site tracking? It depends on the threat model we use.
245 |
246 | A commonly applied threat model posits that both sharing a high-entropy identifier between two sites A and B, and permanent access to cross-site cookies for site B under A, is equivalent, because we assume possible coordination between A and B. When both parties are cooperating, a single high-entropy identifier can be used to establish a communication channel to transfer any amount of information between both parties, at any time (until the user clears all browser state for one of the parties, ignoring fingerprinting).
247 |
248 | However, if we apply a different threat model and assume that some RPs may choose to not cooperate with an IDP to enable cross-site recognition, then they can use FedCM with a single call to navigator.credentials.get(), which will only connect the RP to the IDP a single time, and is thus more private than the permanent ability to make credentialed requests to a cross-site resource. The RP could then even continue to embed iframes from the IdP as long as the ”identity-credentials-get” permissions policy is not set on those.
249 |
250 | There are still a few practical concerns with this idea:
251 |
252 | * In practice, IDPs usually coerce or enforce cooperation, e.g. by providing their own first-party scripts to run on a given RP.
253 | * The existence of partitioned state (e.g. CHIPS) or access to first-party state allows embedded 3Ps to sustain one-time identifiers indefinitely.
254 |
255 | Nonetheless, we propose restricting an IDP’s default access to instances where an RP allows this [through use of the Permissions Policy API](#interaction-with-permissions-policy). This requires explicit RP collaboration for continued access to cross-site state.
256 |
257 | This particularly helps protect against attacks where a large IDP may run additional embedded widgets (a social comment widget, an embedded maps widget, etc.) and would be able to use these to passively track users long after FedCM credentials were passed.
258 |
259 |
260 | ### Consistent user experience
261 |
262 | User agents should make FedCM grants and Storage Access permissions behave consistently, reflecting their similar capabilities, and ensure users understand the difference and similarities between the two.
263 |
264 | There are existing inconsistencies to deal with. In Chrome, the `connections `in the `connected accounts set` do not expire, while the `storage-access` permission expires after 30 days of disuse. This means that a user who grants FedCM permission is now granting perpetual access to unpartitioned cookies, whereas a user who grants storage access directly is only granting access for 30 days. Chrome currently also clears FedCM grants for sites when the user clears site data, but not Storage Access permissions. As user agents we should strive to reduce these inconsistencies where possible.
265 |
266 | Given that FedCM mediates a high-entropy token already, this does not change the privacy properties of this proposal, but is noteworthy nonetheless.
267 |
268 | ## Security Considerations
269 | As [previously discussed in the context of the Storage Access API](https://docs.google.com/document/d/1AsrETl-7XvnZNbG81Zy9BcZfKbqACQYBSrjM3VsIpjY/edit#heading=h.vb3ujl8dnk4q), opening up access to cross-site cookies when those cookies are otherwise blocked by default, comes with additional challenges for website security.
270 |
271 | This proposal does not substantially change the security considerations that already apply to the Storage Access API or FedCM. However, for the proposed integration with requestStorageAccessFor, it’s important to consider the ability for the RP to attack the IDP using [attacks on credentialed cross-site resources that aren't fully mitigated through CORS](https://github.com/privacycg/requestStorageAccessFor/issues/30). The recent [storage access headers](https://github.com/cfredric/storage-access-headers) proposal provides a better alternative that mitigates these attacks and as such the rSAFor integration should be considered with additional scrutiny and may not end up being necessary.
272 |
--------------------------------------------------------------------------------
/storage-access.bs:
--------------------------------------------------------------------------------
1 |
2 | Title: The Storage Access API 3 | Shortname: storage-access 4 | Repository: privacycg/storage-access 5 | URL: https://privacycg.github.io/storage-access/ 6 | Editor: Benjamin VanderSloot, w3cid 135256, Mozilla https://mozilla.org, bvandersloot@mozilla.com 7 | Editor: Johann Hofmann, w3cid 120436, Google https://google.com, johannhof@google.com 8 | Editor: Anne van Kesteren, w3cid 38001, Apple Inc. https://apple.com, annevk@annevk.nl 9 | Former Editor: John Wilander, w3cid 89478, Apple Inc. https://apple.com, wilander@apple.com 10 | Former Editor: Theresa O’Connor, w3cid 40614, Apple Inc. https://apple.com, hober@apple.com 11 | Abstract: The Storage Access API enables content in iframes to request access to website data (such as cookies). 12 | Status Text: 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. 13 | Text Macro: LICENSE Creative Commons Attribution 4.0 International License 14 | Group: privacycg 15 | Status: CG-DRAFT 16 | Level: None 17 | Markup Shorthands: markdown yes, css no 18 | Complain About: accidental-2119 true 19 |20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | spec:html; type:dfn; text:allowed to use 29 | spec:infra; type:dfn; text:user agent 30 | spec:fetch; type:dfn; for:/; text:request 31 |32 | 33 |
34 | urlPrefix: https://fetch.spec.whatwg.org/; spec: Fetch 35 | text: http-network-or-cache fetch; url: #concept-http-network-or-cache-fetch; type: dfn 36 | text: http-redirect fetch; url: #concept-http-redirect-fetch; type: dfn 37 | spec: RFC6265; urlPrefix: https://tools.ietf.org/html/rfc6265 38 | type: dfn 39 | text: cookie store; url: section-5.3 40 | spec: RFC6265bis; urlPrefix: https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-11 41 | type: dfn 42 | text: site for cookies; url: section-5.2.1 43 | urlPrefix: https://w3c.github.io/webdriver/webdriver-spec.html#; spec: webdriver 44 | type: dfn 45 | text: current browsing context; url: dfn-current-browsing-context 46 | text: WebDriver error; url: dfn-error 47 | text: WebDriver error code; url: dfn-error-code 48 | text: extension command; url: dfn-extension-commands 49 | text: extension command URI template; url: dfn-extension-command-uri-template 50 | text: getting a property; url: dfn-getting-properties 51 | text: invalid argument; url: dfn-invalid-argument 52 | text: local end; url: dfn-local-end 53 | text: remote end steps; url: dfn-remote-end-steps 54 | text: unknown error; url: dfn-unknown-error 55 | text: unsupported operation; url: dfn-unsupported-operation 56 | text: session; url: dfn-session 57 | text: success; url: dfn-success 58 | 59 | spec: html; urlPrefix: https://html.spec.whatwg.org/multipage/ 60 | type: dfn 61 | text: source snapshot params; url: browsing-the-web.html#source-snapshot-params 62 | text: snapshotting source snapshot params; url: browsing-the-web.html#snapshotting-source-snapshot-params 63 | text: create navigation params by fetching; url: browsing-the-web.html#create-navigation-params-by-fetching 64 | text: set up a window environment settings object; url: nav-history-apis.html#set-up-a-window-environment-settings-object 65 | text: environment 66 | 67 | text: ancestry; for: environment; url: TODO 68 | 69 | spec: fetch; urlPrefix: https://fetch.spec.whatwg.org/ 70 | type: dfn 71 | for: response 72 | text: has-cross-origin-redirects; url: #response-has-cross-origin-redirects 73 | 74 | spec: fedcm; urlPrefix: https://w3c-fedid.github.io/FedCM/ 75 | type: dfn 76 | text: connected accounts set; url: browser-connected-accounts-set 77 | text: IDP; url: idp 78 | text: RP; url: rp 79 | 80 | spec: credential-management-1; urlPrefix: https://w3c.github.io/webappsec-credential-management/ 81 | type: dfn 82 | text: prevent silent access flag; url: origin-prevent-silent-access-flag 83 | 84 | spec: permissions-policy; urlPrefix: https://w3c.github.io/webappsec-permissions-policy/ 85 | type: dfn 86 | text: is feature enabled; url: algo-is-feature-enabled 87 |88 | 89 |
90 | {
91 | "STORAGE-ACCESS-INTRO": {
92 | "authors": ["John Wilander"],
93 | "date": "February 2018",
94 | "href": "https://webkit.org/blog/8124/introducing-storage-access-api/",
95 | "publisher": "WebKit",
96 | "rawDate": "2018-02-21",
97 | "status": "Blog post",
98 | "title": "Introducing Storage Access API"
99 | }
100 | }
101 |
102 |
103 |
112 |
113 | [=storage access eligibility/unset=]".
184 |
185 | Note: a [=request=]'s [=storage access eligibility=] indicates whether previously-granted "storage-access" permissions ought to be considered when evaluating which cookies to include on the [=request=]. In particular, note that after {{Document/requestStorageAccess}} has resolved and the [=environment=]'s [=environment/has storage access=] is set to true, not all of the [=request=]s issued by that [=environment=] ought to carry unpartitioned cookies.
186 | [=storage access eligibility/unset=]".
194 | 1. If |request|'s [=request/client=]'s [=environment/ancestry=] is not "cross-site", return "[=storage access eligibility/unset=]"
195 |
196 | ISSUE: The concept of "ancestry" is being added to HTML in https://github.com/whatwg/html/pull/11133
197 |
198 | 1. If |request|'s [=request/client=]'s [=environment/has storage access=] is false, return "[=storage access eligibility/ineligible=]".
199 | 1. If |request|'s [=url/origin=] is not [=/same origin=] with |request|'s [=request/url=]'s [=url/origin=], return "[=storage access eligibility/ineligible=]".
200 | 1. Let |allowed| be the result of running [$Should request be allowed to use feature?$] given "storage-access" and |request|.
201 | 1. If |allowed| is false, return "[=storage access eligibility/ineligible=]".
202 | 1. Return "[=storage access eligibility/eligible=]".
203 |
204 |
209 | partial interface Document {
210 | Promise<boolean> hasStorageAccess();
211 | Promise<undefined> requestStorageAccess();
212 | };
213 |
214 |
215 | When invoked on {{Document}} |doc|, the hasStorageAccess() method must run these steps:
216 |
217 |
218 |
219 | 1. Let |p| be [=a new promise=].
220 | 1. If |doc| is not [=Document/fully active=], then [=/reject=] |p| with an "{{InvalidStateError}}" {{DOMException}} and return |p|.
221 | 1. If |doc|'s [=Document/origin=] is an [=opaque origin=], [=/resolve=] |p| with false and return |p|.
222 | 1. Let |global| be |doc|'s [=relevant global object=].
223 | 1. If |global| is not a [=secure context=], then [=/resolve=] |p| with false and return |p|.
224 | 1. If the [=top-level origin=] of |doc|'s [=relevant settings object=] is an [=opaque origin=], [=/resolve=] |p| with false and return |p|.
225 | 1. Let |browsingContext| be |doc|'s [=Document/browsing context=].
226 | 1. Let |topLevelSite| be the result of [=obtain a site|obtaining a site=] from the [=top-level origin=] of |doc|'s [=relevant settings object=].
227 | 1. Let |embeddedSite| be the result of [=obtain a site|obtaining a site=] from |doc|'s [=Document/origin=].
228 | 1. Run the following steps [=in parallel=]:
229 | 1. Let |explicitSetting| be the result of [=determine whether the user agent explicitly allows unpartitioned cookie access|determining whether the user agent explicitly allows unpartitioned cookie access=] with (|topLevelSite|, |embeddedSite|).
230 | 1. Let |permissionState| be the result of [=getting the current permission state=] given "storage-access" and |global|.
231 | 1. [=Queue a global task=] on the [=networking task source=] given |global| to:
232 | 1. If |explicitSetting| is "`disallow`", [=/resolve=] |p| with false.
233 | 1. If |explicitSetting| is "`allow`", [=/resolve=] |p| with true.
234 | 1. [=Assert=]: |explicitSetting| is "`none`".
235 | 1. If |browsingContext| is a [=top-level browsing context=], [=/resolve=] |p| with true.
236 | 1. If |browsingContext| is same authority with |browsingContext|'s [=top-level browsing context=]'s [=active document=], [=/resolve=] |p| with true.
237 |
238 | ISSUE: "same authority" here is a placeholder for a future concept that allows user agents to perform [=/same site=] checks while adhering to additional security aspects such as the presence of a cross-site parent document, see [whatwg/storage#142](https://github.com/whatwg/storage/issues/142#issuecomment-1122147159). In practice, this might involve comparing the [=site for cookies=] or performing a [=/same site=] check with the top-level document.
239 |
240 | 1. If |permissionState| is [=permission/granted=], [=/resolve=] |p| with |global|'s [=environment/has storage access=].
241 |
242 | Note: The global storage access permission state takes precedence over the local [=environment/has storage access=] flag here, in order to immediately reflect a possible user choice to revoke the permission in their settings.
243 |
244 | 1. [=/Resolve=] |p| with false.
245 | 1. Return |p|.
246 |
247 | When invoked on {{Document}} |doc|, the requestStorageAccess() method must run these steps:
248 |
249 |
250 |
251 | 1. Let |p| be [=a new promise=].
252 | 1. If |doc| is not [=Document/fully active=], then [=/reject=] |p| with an "{{InvalidStateError}}" {{DOMException}} and return |p|.
253 | 1. Let |global| be |doc|'s [=relevant global object=].
254 | 1. Let |settings| be |doc|'s [=relevant settings object=].
255 | 1. If |global| is not a [=secure context=], then [=/reject=] |p| with a "{{NotAllowedError}}" {{DOMException}} and return |p|.
256 | 1. If |doc| is not [=allowed to use=] "`storage-access`", [=/reject=] |p| with a "{{NotAllowedError}}" {{DOMException}} and return |p|.
257 | 1. If |doc|'s [=Document/origin=] is an [=opaque origin=], [=/reject=] |p| with a "{{NotAllowedError}}" {{DOMException}} and return |p|.
258 | 1. If |settings|'s [=top-level origin=] is an [=opaque origin=], [=/reject=] |p| with a "{{NotAllowedError}}" {{DOMException}} and return |p|.
259 | 1. If |doc|'s [=active sandboxing flag set=] has its [=sandbox storage access by user activation flag=] set, [=/reject=] |p| with a "{{NotAllowedError}}" {{DOMException}} and return |p|.
260 | 1. Let |browsingContext| be |doc|'s [=Document/browsing context=].
261 | 1. Let |topLevelOrigin| be the [=top-level origin=] of |doc|'s [=relevant settings object=].
262 | 1. Let |topLevelSite| be the result of [=obtain a site|obtaining a site=] from |topLevelOrigin|.
263 | 1. Let |embeddedOrigin| be |doc|'s [=Document/origin=].
264 | 1. Let |embeddedSite| be the result of [=obtain a site|obtaining a site=] from |embeddedOrigin|.
265 | 1. Let |has transient activation| be whether |doc|'s {{Window}} object has [=transient activation=].
266 | 1. Run the following steps [=in parallel=]:
267 | 1. Let |process permission state| be an algorithm that, given a [=permission state=] |state|, runs the following steps:
268 | 1. [=Queue a global task=] on the [=networking task source=] given |global| to:
269 | 1. If |state| is [=permission/granted=]:
270 | 1. Set |global|'s [=environment/has storage access=] to true.
271 | 1. [=/Resolve=] |p| with {{undefined}}.
272 | 1. Else:
273 | 1. [=Consume user activation=] given |global|.
274 | 1. [=/Reject=] |p| with a "{{NotAllowedError}}" {{DOMException}}.
275 | 1. Let |explicitSetting| be the result of [=determine whether the user agent explicitly allows unpartitioned cookie access|determining whether the user agent explicitly allows unpartitioned cookie access=] with (|topLevelSite|, |embeddedSite|).
276 | 1. If |explicitSetting| is "`disallow`":
277 | 1. Run |process permission state| with [=permission/denied=].
278 | 1. Abort these steps.
279 | 1. If |explicitSetting| is "`allow`":
280 | 1. Run |process permission state| with [=permission/granted=].
281 | 1. Abort these steps.
282 | 1. [=Assert=]: |explicitSetting| is "`none`".
283 | 1. If |browsingContext| is a [=top-level browsing context=]:
284 | 1. Run |process permission state| with [=permission/granted=].
285 | 1. Abort these steps.
286 | 1. If |embeddedSite| is [=site/same site=] with |topLevelSite|:
287 |
288 | NOTE: This check is [=site/same site=] on purpose, to allow embedded sites to use `requestStorageAccess()` to opt into storage access without involvement from the end user in scenarios where storage access is restricted for security and not privacy purposes.
289 |
290 | 1. Run |process permission state| with [=permission/granted=].
291 | 1. Abort these steps.
292 | 1. Let |previous permission state| be the result of [=getting the current permission state=] given "storage-access" and |global|.
293 | 1. If |previous permission state| is not [=permission/prompt=]:
294 | 1. Run |process permission state| with |previous permission state|.
295 | 1. Abort these steps.
296 | 1. Let |connected| be the result of [=Determine the effective FedCM connection status|determining the effective FedCM connection status=] given |topLevelOrigin|, |embeddedOrigin|, |doc|.
297 | 1. If |connected|:
298 |
299 | NOTE: User agents are encouraged to keep track of which (site, site) tuples have been allowed to access storage due to existing FedCM connections, and double-check that list when accessing cookies to catch malicious attackers that have tricked an [=environment=] into using an incorrect [=environment/has storage access=] bit.
300 |
301 | 1. Run |process permission state| with [=permission/granted=].
302 | 1. Abort these steps.
303 | 1. If |has transient activation| is false:
304 | 1. Run |process permission state| with [=permission/denied=].
305 | 1. Abort these steps.
306 | 1. Let |permissionState| be the result of [=requesting permission to use=] "storage-access".
307 |
308 | NOTE: Note that when requesting permissions and deciding whether to show a prompt, user agents apply implementation-defined behavior to shape the end user experience. Particularly for `storage-access`, user agents are known to apply custom rules that will grant or deny a permission without showing a prompt.
309 |
310 | 1. Run |process permission state| with |permissionState|.
311 | 1. Return |p|.
312 |
313 | NOTE: The intent of this algorithm is to always require user activation before a storage-access permission will be set. Though it is within the means of user agents to set storage-access permissions based on custom heuristics without prior user activation, this specification strongly discourages such behavior, as it could lead to interoperability issues.
314 |
315 | [=storage access eligibility/unset=]" and locationURL's [=url/origin=] is not [=/same origin=] with |request|'s [=request/current URL=]'s [=url/origin=], set |request|'s [=request/eligible for storage-access=] to "[=storage access eligibility/ineligible=]".
350 | storage-access". It defines the following permission-related algorithms:
379 |
380 | storage-access" permission, given a {{PermissionDescriptor}} |permissionDesc| and a {{PermissionStatus}} |status|:
384 |
385 | 1. Set |status|'s {{PermissionStatus/state}} to |permissionDesc|'s [=permission state=].
386 | 1. If |status|'s {{PermissionStatus/state}} is [=permission/denied=], set |status|'s {{PermissionStatus/state}} to [=permission/prompt=].
387 |
388 | Note: The "denied" permission state is not revealed to avoid exposing the user's decision to developers. This is done to prevent retaliation against the user and repeated prompting to the detriment of the user experience.
389 | storage-access" feature is a [=tuple=] consisting of a [=site=] top-level and a [=site=] requester.
393 |
394 | storage-access" whose [=permission key/top-level=] is `("https", "news.example")` and whose [=permission key/requester=] is `("https", "social.example")`.
397 |
398 | storage-access" feature, given an [=/origin=] |origin| and [=/origin=] |embeddedOrigin|:
403 |
404 | 1. Let |topLevelSite| be the result of [=obtain a site|obtaining a site=] from |origin|.
405 | 1. Let |embeddedSite| be the result of [=obtain a site|obtaining a site=] from |embeddedOrigin|.
406 | 1. Return (|topLevelSite|, |embeddedSite|).
407 | storage-access" feature, run the following steps:
411 |
412 | 1. If |key1|'s [=permission key/top-level=] is not [=site/same site=] with |key2|'s [=permission key/top-level=], return false.
413 | 1. If |key1|'s [=permission key/requester=] is not [=site/same site=] with |key2|'s [=permission key/requester=], return false.
414 | 1. Return true.
415 | storage-access" permission so their express permission is not undermined by fatigue
442 |
443 | This specification requires the first three of implementers. This provides guarantees that the user is aware of the content of the nested {{Document}}, the embedder has not opted out of the nested {{Document}}'s authentication, and the cross-site cookies are not disclosed to network attackers, respectively.
444 |
445 | The last two points are in tension. In an ideal world, we would show a prompt to the user in every call to {{Document/requestStorageAccess()}}. But, this would allow pages to prompt the user so frequently as to put the last point at the discretion of the page– a state we find unacceptable. User agents should prevent over-prompting of the user.
446 |
447 |
450 | storage-access" permission: [=/origin|origins=] or [=site|sites=]. We chose [=site|sites=] because we believe them to be acceptable boundaries for privacy while enabling existing uses of [=site/same site=] and cross-origin nested {{Document|Documents}} on the same page with only one user prompt.
460 |
461 | storage-access" permission grant to only give access to [=unpartitioned data=] to the nested {{Document}} that called {{Document/requestStorageAccess()}} and only until the nested {{Document}} navigates across an [=/origin=] boundary. This ensures that only [=/origin|origins=] with a page that call {{Document/requestStorageAccess()}} will be making credentialed requests, and moreover the embedee page can control which embedder it permits via the Content Security Policy "frame-ancestors" directive. This retains an [=/origin=]-scoped control for security purposes by the embedee.
466 |
467 | | HTTP Method | 487 |URI Template | 488 |
|---|---|
| POST | 491 |/session/{session id}/storageaccess | 492 |