Skip to content

feat(sessions): add setCookies() to restore an exported cookie jar - #202

Merged
sqdshguy merged 5 commits into
masterfrom
feat/set-cookies
Aug 30, 2026
Merged

feat(sessions): add setCookies() to restore an exported cookie jar#202
sqdshguy merged 5 commits into
masterfrom
feat/set-cookies

Conversation

@sqdshguy

Copy link
Copy Markdown
Owner

Closes #201

Problem

getAllCookies() returns cookies with full metadata, but there's no way to put them back. setCookie(name, value, url) only takes a name/value pair, so domain, path, secure, httpOnly, sameSite and expiry are all lost on re-import. That makes a jar export/import round-trip impossible, which is what you need to persist a logged-in session across process restarts.

Changes

session.setCookies(cookies, url?) takes the output of getAllCookies() directly:

const saved = JSON.stringify(session.getAllCookies());

const restored = await createSession({ browser: 'chrome' });
restored.setCookies(JSON.parse(saved), 'https://example.com');

Each cookie is built with cookie::CookieBuilder rather than by formatting a Set-Cookie string, so a value containing ; stays verbatim instead of being reparsed as attributes. The whole batch is validated before anything is stored, so a rejected cookie leaves the jar untouched rather than applying half a restore. Cookies already past expiresAtMs are dropped, matching how the jar treats an expired Set-Cookie.

The url scopes host-only cookies, the ones with no Domain of their own. wreq's jar keeps their origin host as an internal key and doesn't return it from get_all(), so it's absent from the export and has to be supplied. A jar holding host-only cookies from several hosts therefore can't be restored from one url; SessionCookieInit also accepts a per-cookie url for that case. Upstream fix proposed in 0x676e67/wreq#1269, which would let the export carry the host and make both arguments unnecessary.

Two other fixes came out of this:

  • getAllCookies() now reports sameSite: "none". wreq's Cookie wrapper only exposes same_site_lax()/same_site_strict(), so SameSite=None was indistinguishable from an absent attribute and exported as undefined. Real servers use it: upwork.com sends it on __cf_bm, __cflb and AWSALBTGCORS. The export now reads the attribute off the raw cookie.
  • expiresAtMs no longer drifts by a millisecond per round-trip. Epoch nanoseconds exceed f64's exact integer range, so ms * 1e6 lost precision; whole and fractional milliseconds are now scaled separately.

Tests

Five added to src/test/http/sessions.spec.ts: attribute-preserving restore through a JSON round-trip plus a live request, per-cookie scoping across two hosts, rejected-batch atomicity, expired-cookie drop, every SameSite value, and setCookies on a disposed session. 199 pass.

Checked against tough-cookie 6.0.2 as an oracle: 15 Set-Cookie shapes compared field by field, including SameSite=None, lowercase samesite=none, uppercase SAMESITE=STRICT and an invalid SameSite=Bogus, plus cookie selection across 6 URLs covering Secure filtering, path matching, domain matching and host-only scoping. All agree.

Also verified against real sites: a 17-cookie jar spanning httpbingo.org, google.com, github.com and upwork.com round-trips byte-identical, restored __cf_bm is accepted by Cloudflare (200 on patreon.com, openai.com, upwork.com), and a jar written to disk in one process restores in another with the origin server seeing identical cookies.

Docs

docs/api-reference/sessions.mdx and docs/concepts/sessions.mdx.

@sqdshguy
sqdshguy merged commit f24b129 into master Aug 30, 2026
9 checks passed
@sqdshguy
sqdshguy deleted the feat/set-cookies branch August 30, 2026 13:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature request: add setCookies() method to accept array of cookie objects from getAllCookies()

1 participant