Skip to content

Storage Access API: requesting third-party cookie access from an iframe

In one line: The Storage Access API “provides a way for cross-site content loaded in a third-party context (i.e., embedded in an <iframe>) to gain access to third-party cookies and unpartitioned state that it would typically only have access to in a first-party context,” for browsers that block unpartitioned third-party cookies by default.

Browsers restrict access to unpartitioned third-party cookies — “all cookies for a site stored in one shared jar” — in a third-party <iframe> context to prevent cross-site tracking. That breaks legitimate cases such as single sign-on with a federated identity provider or a personalization service embedded across multiple related sites. The Storage Access API is, per MDN, “intended to solve this problem” without requiring users to disable third-party cookie blocking outright.

const hasAccess = await document.hasStorageAccess();
if (!hasAccess) {
await document.requestStorageAccess();
}

Document.hasStorageAccess() resolves to a boolean indicating whether the document already has access to third-party cookies. Document.requestStorageAccess() requests the storage-access permission and resolves if granted, or rejects if denied. Document.hasUnpartitionedCookieAccess() is documented as the new name for hasStorageAccess().

Per MDN: “requestStorageAccess() requests are automatically denied unless the embedded content is currently processing a user gesture such as a tap or click (transient activation), or if permission was already granted previously.” That is why a real integration calls it from inside a click handler rather than on page load.

function doThingsWithCookies() {
document.cookie = "foo=bar";
}
async function handleCookieAccess() {
if (!("requestStorageAccess" in document)) {
// Storage Access API unsupported here — fall back to hoping cookies work,
// as recommended by MDN for browsers without this API.
doThingsWithCookies();
return;
}
const hasAccess = await document.hasStorageAccess();
if (hasAccess) {
doThingsWithCookies();
return;
}
btn.addEventListener("click", async () => {
try {
await document.requestStorageAccess();
doThingsWithCookies();
} catch (err) {
console.error(`Error obtaining storage access: ${err}. Please sign in.`);
}
});
}

A sandboxed <iframe> must carry the allow-storage-access-by-user-activation sandbox token (along with allow-scripts and allow-same-origin) for a requestStorageAccess() call from inside it to succeed:

<iframe
sandbox="allow-storage-access-by-user-activation
allow-scripts
allow-same-origin"
>
</iframe>

A grant is keyed by <top-level site, embedded site> — MDN gives the example of embedding locator.example.com on embedder.com: the grant covers “any subdomain of example.com embedded on any page of embedder.com.” Even after a grant exists, each new browsing context (a new tab, or a fresh iframe instance) must call requestStorageAccess() again to activate it there — though MDN notes that call “won’t require transient activation once already granted.” A same-origin self-navigation or reload after being granted access carries the access over without a new call.

MDN lists several conditions under which requestStorageAccess() is denied: the document and top-level document must not have a null origin; the window must be a secure context (HTTPS); the embedded origin must have interacted with the browser as first-party recently; and access “can be blocked by a storage-access Permissions Policy header set by the server.” MDN also notes the document “may also be required to pass additional browser-specific checks” such as allowlists, blocklists, or user settings.

MDN documents Document.hasStorageAccess, Document.hasUnpartitionedCookieAccess, Document.requestStorageAccess, and the non-standard, deprecated Document.requestStorageAccessFor each with their own browser compatibility table; check those tables for the current per-browser support position before depending on this API in production.

  • Feature-detect with if (!document.hasStorageAccess) before calling any method — MDN’s own example falls back to just trying the cookie operation when the API is absent.
  • Call requestStorageAccess() from inside a user-gesture event handler (e.g. a click), not on page load — it is denied outside transient activation unless already granted.
  • Wrap requestStorageAccess() in try...catch; the returned promise rejects when the user denies access or the gesture requirement is not met.
  • Add the allow-storage-access-by-user-activation sandbox token to a sandboxed <iframe> that needs to call this API, alongside allow-scripts and allow-same-origin.
  • Re-call requestStorageAccess() in each new tab or iframe instance even after a grant exists elsewhere for the same <top-level site, embedded site> pair.
  • Note that Document.requestStorageAccessFor() is non-standard and deprecated; it lets a top-level site request access, for an embed in the same related website set, on behalf of that embed instead of the embed calling the API itself.