Intent to Ship: Extending Storage Access API (SAA) to non-cookie storage

1,000 views
Skip to first unread message

Ari Chivukula

unread,
Mar 14, 2024, 9:27:20 AMMar 14
to blink-dev, Ben Kelly, Johann Hofmann, ros...@google.com

Contact emails

ari...@chromium.org, wande...@chromium.org, joha...@chromium.org, ros...@google.com


Specification

https://privacycg.github.io/saa-non-cookie-storage/


Design Doc

https://docs.google.com/document/d/19qCGb4qwOcGiNrQM3ptWvRmB4JtpaTFgFVlWLXNOQ6c/edit


Feedback

https://github.com/privacycg/saa-non-cookie-storage/issues


Intent to Prototype

https://groups.google.com/a/chromium.org/g/blink-dev/c/inRN8tI49O0


Intent to Experiment

https://groups.google.com/a/chromium.org/g/blink-dev/c/SEL7N-xIE5s

https://groups.google.com/a/chromium.org/g/blink-dev/c/AjH7tGxuVuw


Summary

This launches the proposed extension of the Storage Access API (backwards compatible and currently in OT) to allow access to unpartitioned cookie and non-cookie storage in a third-party context. The current API only provides access to cookies, which have different use-cases than non-cookie storage (discussed more in the Motivation section). The API can be used as follows (JS running in an embedded iframe):


// Request a new storage handle via rSA (this may prompt the user)

let handle = await document.requestStorageAccess({all: true});

// Write some 1P context sessionstorage

handle.sessionStorage.setItem("userid", "1234");

// Write some 1P context localstorage

handle.localStorage.setItem("preference", "A");

// Open or create an indexedDB that is shared with the 1P context

let messageDB = handle.indexedDB.open("messages");

// Use locks shared with the 1P context

await handle.locks.request(“example”, …);


The same flow would be used by iframes to get a storage handle when their top-level ancestor successfully called requestStorageAccessFor, just that in this case the storage-access permission was already granted and thus the requestStorageAccess call would not require a user gesture or show a prompt, allowing for “hidden” iframes accessing storage. 


Beyond calling this additional extension, access to non-cookie storage would match the current requirements for cookie access through the Storage Access API.


DOM Storage (session and local storage), Indexed DB, Web Locks, Cache Storage, Origin Private File System, Quota, Blob Storage, Broadcast Channel, and SharedWorkers will be available.


Blink component

Blink>StorageAccessAPI


Motivation

There has been increasing developer and implementer interest in first-party DOM Storage and Quota Managed Storage being available in third-party contexts the same way that cookies can be today. In the absence of such a solution, browsers would in effect be pushing developers to migrate to cookies from other storage mechanisms. There are tradeoffs between cookie and non-cookie storage (size, flexibility, server exposure, network request size, etc.) that could impact user experience from a privacy, security and performance perspective (e.g., cookies are included in HTTP requests and not just available via JavaScript). To prevent sub-optimal use of cookies and to preserve context, we propose a solution for developers to regain 3p access to unpartitioned storage to avoid user-facing breakage in browsers shipping storage partitioning.


TAG review

https://github.com/w3ctag/design-reviews/issues/906


Compatibility

The Storage Access API is already implemented in Safari, Firefox, and Chrome, but the proposed API shape would preserve existing behavior until the web developer adds new arguments.


Interoperability

Gecko: No Position Yet https://github.com/mozilla/standards-positions/issues/898

WebKit: No Position Yet https://github.com/WebKit/standards-positions/issues/262

Web developers: Positive


Debuggability

Storage written can be examined in devtools.


Is this feature fully tested by web-platform-tests?

Yes


Tracking bug

https://issues.chromium.org/40282415


Link to entry on the Chrome Platform Status

https://chromestatus.com/feature/5175585823522816


Domenic Denicola

unread,
Mar 14, 2024, 9:47:22 PMMar 14
to Ari Chivukula, blink-dev, Ben Kelly, Johann Hofmann, ros...@google.com
This appears to be (a rendered version of) an explainer, not a specification.


--
You received this message because you are subscribed to the Google Groups "blink-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to blink-dev+...@chromium.org.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/blink-dev/CAGpy5DKXaZ4S%2B2W6GB_cpuE5i_UDBOMCwcuUYt9Qh1PpQz%3D11w%40mail.gmail.com.

Ari Chivukula

unread,
Mar 19, 2024, 4:05:57 PMMar 19
to Domenic Denicola, blink-dev, Ben Kelly, Johann Hofmann, ros...@google.com
Sorry about that, it's been resolved and https://privacycg.github.io/saa-non-cookie-storage/ is now a draft spec.

~ Ari Chivukula (Their/There/They're)

Yoav Weiss (@Shopify)

unread,
Mar 20, 2024, 7:29:35 AMMar 20
to blink-dev, Ari Chivukula, Ben Kelly, Johann Hofmann, ros...@google.com
Despite the lack of an official position, the discussion seems encouraging. I sympathize with concerns around "all"'s semantics and their future compat impact.
Was this point discussed in the CG or elsewhere?

Ari Chivukula

unread,
Mar 20, 2024, 7:40:31 AMMar 20
to Yoav Weiss (@Shopify), blink-dev, Ben Kelly, Johann Hofmann, ros...@google.com
I think the last place it came up was in this thread: https://github.com/mozilla/standards-positions/issues/898#issuecomment-1745688352


Either way, there hasn't been further concern raised recently so we moved forward with 'all' since the function is async (the delay from loading local/session storage isn't high, and it was often already loaded given the requirement to have interacted with the iframe's site in a top-level context in the past).


~ Ari Chivukula (Their/There/They're)

Yoav Weiss (@Shopify)

unread,
Mar 20, 2024, 7:48:23 AMMar 20
to Ari Chivukula, blink-dev, Ben Kelly, Johann Hofmann, ros...@google.com
On Wed, Mar 20, 2024 at 12:40 PM Ari Chivukula <ari...@chromium.org> wrote:
I think the last place it came up was in this thread: https://github.com/mozilla/standards-positions/issues/898#issuecomment-1745688352


Either way, there hasn't been further concern raised recently so we moved forward with 'all' since the function is async (the delay from loading local/session storage isn't high, and it was often already loaded given the requirement to have interacted with the iframe's site in a top-level context in the past).

The only concern I may have on the "all" front is related to changing semantics. How likely are we to add future storage mechanisms that users would not want to expose along with current ones?

Ari Chivukula

unread,
Mar 20, 2024, 7:57:06 AMMar 20
to Yoav Weiss (@Shopify), blink-dev, Ben Kelly, Johann Hofmann, ros...@google.com
I'd guess that, in the future, the semantics around 'all' may change in one of two ways:

(A) If storage methods included are deprecated, (1) we would start warning developers via a DevTools issue when they use 'all', (2) we would start requiring the deprecated method was specifically included in the call to rSA rather than it being automatically included in all, (3) the storage method would be removed from rSA. The timeline for this would likely be rather long (alongside the timeline for the deprecation of the storage method itself).

(B) If a new storage method were proposed, (1) we would allow developers to use it if explicitly included in rSA (but not add it via all) and then (2) add it under 'all' once it had fully launched.

The chances of a new storage method being added we (1) do want in rSA but (2) wouldn't ever want under 'all' is low I think. All of the storage/communication mechanisms besides local/session storage either have async APIs or don't expose events to monitor changes that would require full loading of data simply because the handle/constructor was made available. I agree there is a potential for a footgun here, but given the direction these APIs seem to be heading I don't think the risk is high. What are the chances vendors decide to add new storage mechanisms that are loaded at document initialization with all data synchronously available?

~ Ari Chivukula (Their/There/They're)

Yoav Weiss (@Shopify)

unread,
Mar 20, 2024, 11:14:46 AMMar 20
to Ari Chivukula, blink-dev, Ben Kelly, Johann Hofmann, ros...@google.com
LGTM1

The signals from other vendors and the CG discussion seem encouraging and I agree that the future risk from an "all" value is probably outweighed by its developer-facing benefits. 

Daniel Bratell

unread,
Apr 3, 2024, 11:30:12 AMApr 3
to Yoav Weiss (@Shopify), Ari Chivukula, blink-dev, Ben Kelly, Johann Hofmann, ros...@google.com

LGTM2

/Daniel

--
You received this message because you are subscribed to the Google Groups "blink-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to blink-dev+...@chromium.org.

Rick Byers

unread,
Apr 3, 2024, 11:57:15 AMApr 3
to Daniel Bratell, Yoav Weiss (@Shopify), Ari Chivukula, blink-dev, Ben Kelly, Johann Hofmann, ros...@google.com

Ari Chivukula

unread,
May 24, 2024, 12:29:49 PMMay 24
to Rick Byers, Daniel Bratell, Yoav Weiss (@Shopify), blink-dev, Ben Kelly, Johann Hofmann, ros...@google.com
Noting this here for future discoverability: one of the APIs that was launched as part of this was a new name for hasStorageAccesshasUnpartitionedCookieAccess. This was documented in the linked explainer and spec, but failed to make the text of the I2E/S as it probably should have.

This was included to make the meaning of the boolean returned by the function clearer, as hasStorageAccess does not indicate if access to non-cookie storage via requestStorageAccess was granted, it only indicates if unpartitioned cookies are available in the given context (which may have been granted via a requestStorageAccess call).

hasUnpartitionedCookieAccess is effectively an alias for hasStorageAccess, and there are no plans to remove hasStorageAccess as far as I'm aware.

For example:
// In a cross-site iframe with third-party cookies disabled
let access1 = await document.hasStorageAccess();
let access2 = await document.hasUnpartitionedCookieAccess();
// access1 equals access2 equals false.

// Request access only to unpartitioned localStorage
let handle = await document.requestStorageAccess({localStorage: true});
access1 = await document.hasStorageAccess();
access2 = await document.hasUnpartitionedCookieAccess();
// access1 equals access2 equals false.

// Request access to unpartitioned cookies as well
await document.requestStorageAccess();
// the above is similar to calling requestStorageAccess({cookies: true})
access1 = await document.hasStorageAccess();
access2 = await document.hasUnpartitionedCookieAccess();
// access1 equals access2 equals true.

~ Ari Chivukula (Their/There/They're)

Reply all
Reply to author
Forward
0 new messages