Back to Knowledge Hub
Identity

Cookie Store API: Async Cookies, Change Events, and Privacy Boundaries

Use the Cookie Store API for asynchronous cookie work, reason about change events and service-worker visibility, and keep origin and privacy boundaries explicit.

BotBrowser Team

Documentation

Want the structured docs for Identity?

This article lives in the editorial library. For step-by-step setup, reference material, and ongoing updates, jump into the docs section.

Cookies look small in source code, but their lifecycle crosses several boundaries: an asynchronous API call, an event delivered to eligible contexts, a service-worker scope, and the browser's origin and policy rules. The Cookie Store API makes cookie operations promise-based and exposes a change event, yet it does not turn cookies into shared application state or grant another origin access.

This guide builds a control/candidate fixture for an owned site. It separates what a page can request, what a service worker can observe, and what the browser keeps outside the origin. MDN's Cookie Store API reference and the WICG specification are the behavioral sources. BotBrowser can repeat the authorized fixture with a declared profile; it cannot change cookie policy or prove a server-side account state.

Summary

BotBrowser can replay the page and worker fixture with synthetic cookies; it cannot override browser cookie policy or prove a server-side account. The browser knowledge boundary is the WICG Cookie Store specification and MDN's implementation guidance.

The API is asynchronous, events are local observations, and a worker must reconcile after activation. Origin and cookie attributes remain the controlling boundaries. Use a synthetic marker and retain only redacted outcomes.

Contents

  • Async operations
  • Change events
  • Service-worker visibility
  • Privacy boundaries
  • Practical conclusion

Page and service worker use asynchronous Cookie Store API operations and change events within one origin, while another origin is blocked by an explicit boundary

Async operations

cookieStore.get(), getAll(), set(), and delete() return promises. A promise makes the operation non-blocking, not unrestricted. The browser still applies origin, scheme, path, domain, Secure, SameSite, and user-policy rules. A resolved set() means the browser accepted the requested mutation in this context; it is not a receipt that every later request, tab, or server will observe the value.

Use a named operation and handle rejection as a product state. Feature-detect window.cookieStore, validate the cookie name and attributes, and keep the value out of logs. A candidate fixture should show supported, written, read, deleted, and rejected as separate outcomes. Do not collapse a missing API and a policy rejection into “cookies are broken.”

async function writeSessionMarker(value) {
  if (!('cookieStore' in window)) return { status: 'unsupported' };
  try {
    await cookieStore.set({ name: 'fixture_marker', value, sameSite: 'lax' });
    const item = await cookieStore.get('fixture_marker');
    return { status: item?.value === value ? 'written' : 'not-observed' };
  } catch (error) {
    return { status: 'rejected', name: error?.name ?? 'UnknownError' };
  }
}

The API does not replace document.cookie in every release or context. Keep the fallback deliberate and equivalent: if the asynchronous API is unavailable, use the legacy path only for the narrow operation your product already supports, and preserve the same expiry, Secure, and SameSite contract. Never use a fallback to widen scope.

Change events

The cookieStore change event reports cookie changes visible to the current context. Its changed and deleted collections identify names and metadata, so a page can refresh derived UI without polling. The event is not a network webhook, a complete audit log, or proof that a server received a cookie. A write performed by one context can produce an event in another eligible context, but delivery timing and eligibility remain browser behavior.

Register one listener, debounce expensive rendering, and treat duplicate or reordered UI refreshes as possible. Read the current cookie after an event instead of trusting an old value embedded in the event. For sensitive cookies, record only the name and outcome. Do not place token values in analytics, screenshots, or exception messages.

Service-worker visibility

The Cookie Store API is designed to work in secure contexts and can be used by service workers where the browser implements the relevant surface. A worker's visibility is bounded by its registration and origin. It does not receive cookies for unrelated origins, and its scope does not let it read a page outside that origin. A worker can coordinate cache or notification behavior after a cookie change, but it cannot become an identity authority.

Coordinate page and worker state with an explicit message or a shared event handler. On activation, read the current state; do not assume that an event was delivered while the worker was stopped. On shutdown or update, tolerate a missed notification and reconcile from the source of truth. A service worker update also changes which script is active, so record the worker version alongside a fixture result.

Privacy boundaries

Origin is the primary boundary: scheme, host, and port define which script can address a cookie store. Domain and Path further constrain where a cookie is sent, while SameSite constrains cross-site requests. HttpOnly cookies are intentionally unavailable to script, including Cookie Store API calls. Secure cookies require a secure context. These attributes are not optional decoration; they are part of the data-flow contract.

Do not infer a person's identity from a cookie name, a successful read, or a change event. A profile can contain multiple users over time, a cookie can expire or be cleared, and a browser can partition or block state. Review consent, retention, logout, deletion, and server validation separately. A browser fixture demonstrates an observable client result, not legal compliance or account ownership.

Use an owned HTTPS page and an owned service worker under the same origin. Start from a clean profile, set a test-only marker, read it, delete it, and reload. Run the same sequence with the API absent or with a cookie attribute that the browser rejects. The control records browser release, profile label, secure-context status, worker registration, cookie attributes, and visible outcome. The candidate changes one variable at a time: API support, worker lifecycle, or attribute policy.

The acceptance table should distinguish these states:

StateObservable resultInterpretation
UnsupportedcookieStore is absentUse the bounded fallback; do not claim browser-wide support.
Writtenset() resolves and a fresh get() matchesThis origin accepted this mutation in this context.
ChangedListener runs with the expected nameA visible change was delivered; it is not a server receipt.
Deleteddelete() resolves and get() is emptyThe client no longer observes the marker under these conditions.
RejectedPromise rejects or attribute is filteredPreserve the task and show a defined signed-out or retry state.

BotBrowser supports authorized cookie injection for a declared browser context through --bot-cookies. That can establish a known starting state for a control run. It does not grant Cookie Store API permission, expose HttpOnly values, override SameSite, or make a worker observe another origin. Keep injected values synthetic and delete fixtures after the run.

Release checklist

Re-run the fixture after a browser major release, a service-worker script change, cookie-attribute change, login/logout change, or profile policy change. Keep the source page, worker script, and expected result versioned together. Compare visible outcomes and event delivery, not timing alone. If the worker misses an event while stopped, the candidate should reconcile on activation; that is a lifecycle result, not evidence of data loss.

For adjacent boundaries, read Storage Access API for Embedded Content and Cookie Management. Those guides cover embedded access and test setup; this article remains limited to the asynchronous Cookie Store API and its origin-scoped observations.

Practical conclusion

Keep the operation asynchronous, reconcile state when a worker starts, and record the exact browser and cookie attributes. Treat every result as a scoped browser observation.

Decision table

QuestionRecordDo not infer
Did set() resolve?Current origin accepted a mutationServer persistence or user identity
Did change fire?An eligible context observed a changeA complete audit trail
Did the worker read a marker?Worker lifecycle and scope were activeAccess to another origin
Did delete clear the read?Current context no longer sees the markerErasure from every backup or service

The table is intentionally narrow. It keeps a browser receipt separate from application, account, and retention decisions. Repeat it with the same fixture after a release change, then compare the declared browser and profile rather than merging results into a universal claim.

The control run should include a no-API candidate, a rejected attribute candidate, a stopped-worker candidate, and a second-origin candidate. Each row gets one expected visible result and one owner. A failed row is actionable only when the fixture records the context that produced it.

For production rollout, expose a bounded fallback, preserve the user's current task, and make logout or deletion explicit. Do not silently retry a rejected operation or copy a value into a less protected cookie. Keep test markers short-lived and synthetic.

The browser may change prompt, partition, or event timing behavior between releases. That is why the evidence packet names the browser version, secure context, worker version, attributes, and outcome. It does not need a cookie value to answer the support question.

The fixture is most useful when its starting conditions are explicit. Record whether the page was loaded as a top-level document, whether the worker had already completed activation, and whether the marker was present before the first operation. A test that begins with an unknown profile can report a successful read while hiding the write that produced it. Reset the profile or delete the marker before each run, then verify the empty state with a fresh read. Keep a second profile for a returning-session case so that a warm browser is not mistaken for a clean browser.

The cookie attributes belong in the fixture input, not only in the debugging notes. Test a host-only cookie and a deliberately scoped Domain cookie separately. Test a path that matches the page and a path that does not. Test SameSite=Lax for a normal first-party journey and a cross-site request that should not carry it. A Secure cookie should be tested over HTTPS; an insecure development page should be reported as a different environment, not silently treated as equivalent. HttpOnly belongs in a server-set control because its absence from script is the expected result.

Make the result understandable to the person operating the feature. Show a pending state while an asynchronous write is in flight, keep the existing preference visible until a fresh read confirms the change, and offer a clear retry or fallback when the browser rejects it. The fixture should also capture whether the page was top-level, whether consent was already recorded, and whether a logout action had just removed the marker. Those details prevent a support report from treating a warm profile as a clean baseline. They also make comparisons between browser releases more useful because each result names the conditions that produced it. A concise status such as “saved in this browser context” is more accurate than “account updated” until the server has independently acknowledged the request.

Event handling needs an ownership rule. The page owns the visible account or preference state, while the worker owns background coordination such as a notification or cache decision. Neither should copy the complete cookie value into a message when a name and a redacted outcome are sufficient. If both contexts receive a change, let the page reread its own state and let the worker reread its own state. This costs a small amount of work and avoids making event data an accidental source of truth.

A worker can be terminated between two operations. That is normal lifecycle behavior, not necessarily a failure. When it starts again, it should register its listener, read the current marker, and record the worker script revision. If a page sends a message before activation, the page should retain the user action or show a bounded pending state rather than assuming that the message was processed. The fixture should include a reload during activation and a browser restart between write and read. Those cases reveal whether the application has a recovery path.

The candidate matrix should include a browser with no cookieStore property, a browser that exposes the property but rejects a requested attribute, and a browser where the API resolves but an event is not observed by a stopped worker. These are different outcomes. The first is capability absence. The second is policy or attribute handling. The third is lifecycle timing. A product message can stay simple while the evidence remains precise: “This browser cannot use the asynchronous path,” “The browser rejected this cookie configuration,” or “Background state will be reconciled when the worker starts.”

Do not turn the marker into a tracking identifier. Use a random value that is valid only for the test, give it a short expiry, and remove it at the end of the run. The evidence should contain the marker name, an operation status, and a timestamp relative to the run, but never the marker value. If a support case needs to compare two profiles, label them control-a and candidate-b rather than using an account address. Delete the profile and server fixture according to the test retention policy after the result is accepted.

The server side has its own contract. A browser write can be followed by a request that carries the cookie, but that request still passes through routing, authentication, CSRF protection, and server session validation. A missing cookie can mean that the browser filtered it, that the request was cross-site, that the path did not match, or that the server intentionally cleared it. The client fixture therefore records the browser result and the request fixture records the redacted server observation as separate rows. Never promote one row into proof of the other.

Accessibility belongs in the same acceptance record. When a write is pending, announce a short status and keep the current control usable. When it is rejected, explain the supported alternative without exposing an engine exception. When a change refreshes a preference, keep focus stable and expose the new value to assistive technology. A worker-driven update should produce the same visible and announced state as a page-driven update. This makes the boundary understandable to users instead of leaving it as an internal browser detail.

Release review should compare one variable at a time. Hold the page, worker, cookie attributes, synthetic profile, and fixture inputs constant while changing the browser release. Hold the browser constant while changing the worker script. Hold both constant while changing the cookie policy. Store the previous accepted record and link a new record to it with the reason for the change. If the event is delivered at a different point in the lifecycle, update the support matrix and the user-facing recovery behavior rather than claiming that the API is universally broken or universally fixed.

This approach also keeps BotBrowser evidence in proportion. A declared profile and synthetic cookie make a browser journey repeatable. They do not grant a site authority over another origin, change a user's browser settings, expose server-only state, or establish a person's identity. The useful result is narrower: in this profile, on this release, under this origin and these attributes, the page and worker observed the stated outcome. That is enough to decide whether to ship a fallback, adjust a worker reconciliation step, or investigate a server boundary.

Sources

#Cookie Store Api#Cookies#Service Workers#Browser Privacy#Web Platform

Take BotBrowser from research to production

The guides cover the model first, then move into cross-platform validation, isolated contexts, and scale-ready browser deployment.