API in Storage
Document.requestStorageAccess()
Published
document.requestStorageAccess() asks the browser to give a document loaded in a cross-site
<iframe> access to its own unpartitioned cookies, the ones it would see as a first party, after
the browser has blocked or partitioned them. document.hasStorageAccess() reports whether the
document has that access now. The API exists so that embedded sign-in, payment, and comment
widgets keep working in Firefox, Safari, and Chrome without third-party cookies being re-enabled
site-wide.
Syntax
Section titled “Syntax”document.hasStorageAccess()document.requestStorageAccess()document.requestStorageAccess(types)document.hasUnpartitionedCookieAccess()hasStorageAccess() and hasUnpartitionedCookieAccess() resolve to a boolean;
requestStorageAccess() resolves with no value when access is granted and rejects otherwise.
Support: hasStorageAccess() and requestStorageAccess() in Firefox 65, Safari 11.1, Chrome 119;
the types argument in Chrome 125 only; hasUnpartitionedCookieAccess() in Chrome 125 only (BCD
api.Document.requestStorageAccess). Safari grants client-side storage access per page rather
than per frame (BCD note on api.Document.requestStorageAccess), so a grant obtained in one
iframe also applies to a sibling iframe of the same embedded site on that page.
Platform support
Section titled “Platform support”Safari shipped the API first (11.1, 2018) and Firefox followed in 65; Chrome exposed it in 119
once third-party cookie restrictions made it necessary (BCD api.Document.requestStorageAccess).
The types extension and hasUnpartitionedCookieAccess() are Chrome 125+ only. Android WebView
has no implementation, so an embedded sign-in widget inside a native app’s WebView falls through
to the no-API branch of the detection example.
Parameters
Section titled “Parameters”| Parameter | Type | Meaning |
|---|---|---|
types |
object, optional (Chrome 125+) | Which unpartitioned storage to expose. Boolean members: all, cookies, sessionStorage, localStorage, indexedDB, locks, caches, getDirectory, estimate, createObjectURL, revokeObjectURL, BroadcastChannel, SharedWorker. Omitted: cookies only. |
With types, the promise resolves to a StorageAccessHandle whose members (handle.localStorage,
handle.indexedDB, and so on) are the unpartitioned objects; the document’s own globals stay
partitioned. Without types, the grant applies to cookies on subsequent requests made by the
iframe and to document.cookie.
Exceptions
Section titled “Exceptions”| Exception | When |
|---|---|
InvalidStateError DOMException |
The document is not fully active, or types was passed with every member false. |
NotAllowedError DOMException |
The window is not a secure context; the storage-access Permissions Policy blocks the feature; the document or the top-level document has an opaque origin; the iframe is sandboxed without allow-storage-access-by-user-activation; the call did not run during transient user activation and no grant exists yet; the user denied the prompt; or an engine-specific check failed (Safari requires prior first-party interaction with the embedded site, Chrome applies its site-pair heuristics). |
The user-activation case is the one developers hit first. Chrome’s Console message for it reads
requestStorageAccess: Must be handling a user gesture to use.
(storage_access_grant_permission_context.cc,
github.com). A document that already holds a grant for the current <top-level site, embedded site>
pair may call the method outside a gesture; each new iframe instance or tab still has to call it
once to activate the grant in that context.
Examples
Section titled “Examples”All three examples run inside the embedded document, which is the only place the API does anything.
Gating a sign-in widget on storage access
Section titled “Gating a sign-in widget on storage access”Check first, request only from a click, and keep the whole widget usable without cookies until the promise resolves. The button is the activation the API needs.
async function initWidget(button) { if (await document.hasStorageAccess()) { return renderSignedIn(); } renderSignedOut(); button.addEventListener('click', async () => { try { await document.requestStorageAccess(); renderSignedIn(); // document.cookie now includes the first-party session cookie } catch (err) { if (err.name === 'NotAllowedError') renderDeniedHint(); else throw err; } });}Safari shows a prompt naming both sites the first time; Firefox shows one and remembers the answer for the site pair; Chrome decides without a prompt for pairs in the same Related Website Set and prompts otherwise (Using the Storage Access API, developer.mozilla.org). The code is the same in each case.
Reading unpartitioned localStorage through a handle
Section titled “Reading unpartitioned localStorage through a handle”With types, Chrome returns a handle rather than changing the globals. Use the handle for the
unpartitioned data and leave window.localStorage for state that should stay per top-level site.
async function loadSharedPrefs() { const handle = await document.requestStorageAccess({ localStorage: true }); return JSON.parse(handle.localStorage.getItem('prefs') ?? '{}');}The call still requires activation and a secure context, and it rejects with InvalidStateError
if every requested type is false; request only the types the widget reads.
Detecting the API and degrading to partitioned state
Section titled “Detecting the API and degrading to partitioned state”Engines without the API have either unpartitioned third-party cookies (older Chrome with cookies allowed) or no cross-site cookies at all. Both cases are handled by trying the cookie-dependent path and treating failure as signed out.
async function cookieAccess() { if (!('requestStorageAccess' in document)) { return 'unknown'; // no Storage Access API: proceed as if cookies may or may not arrive } if (await document.hasStorageAccess()) return 'granted'; return 'needs-gesture'; // show a button and call requestStorageAccess() from its click}A sandboxed <iframe> needs allow-storage-access-by-user-activation together with
allow-scripts and allow-same-origin in its sandbox attribute, or every request rejects with
NotAllowedError regardless of the gesture.
See also
Section titled “See also”- The Storage Access API: requestStorageAccess() (privacycg.github.io)
- Document: requestStorageAccess() method (developer.mozilla.org)
- Partitioned cookies (CHIPS)
- localStorage and sessionStorage
- Credential Management API
Specifications
| Specification | Status |
|---|---|
| None. | |