Repository navigation
fix(native): enforce Secure/Path/Expires cookie attributes in the Swift and Dart cookie jars - #976
Conversation
…nd Dart The Swift and Dart cookie jars now keep each cookie's Path, Secure flag and expiry when storing a Set-Cookie response, and apply them when attaching cookies to a request, following the Kotlin runtime: a cookie is sent only to the host that set it, only to request paths under its Path, and Secure cookies only over https/wss. Max-Age takes precedence over Expires, and Max-Age <= 0 or a past Expires removes the cookie. A new cookie replaces the same-named cookies that would be sent on that request. Swift moves the jar logic into StoredCookie and CookieJar over a CookieItemStore protocol; Keychain items hold a JSON record keyed by host|path|name, and items written under the earlier host|name layout are still read. Dart keys cookies by host, path and name, adds SessionStore.cookieHeaderFor and a requestUri argument to setCookies, and persists a list of records while still reading the earlier name -> value map.
|
…icity Swift and Dart cookie path matching now sends a cookie only when the request path equals its Path or continues past it at a "/", so a cookie for /admin/ is no longer attached to /admin. Same-named cookies are listed longest Path first in the Cookie header. The Swift CookieJar runs its read and replace steps under one lock so concurrent responses apply in sequence. The PersistentSessionStore docstring now describes the JSON list format it writes.
cadivus
left a comment
There was a problem hiding this comment.
Approved but I am not sure if the two comments. Eventually look into that.
… them After a followed redirect, Set-Cookie headers belong to the last URL in the chain. Swift now uses HTTPURLResponse.url, and Dart sends cookie- carrying POSTs through a helper that reads BaseResponseWithUrl.url, falling back to the requested URL.
Treat a Set-Cookie with an empty value as a removal in the Dart cookie jar, matching the Swift jar. Document and test that cookies read from the earlier name-to-value format carry no host until the server sets them again.
|
Both items from the review are addressed in 07fdef7. Empty-value cookies ( Host scoping of cookies read from the earlier format (
Verification (Dart 3.11.6, |
…ributes-swift-dart # Conflicts: # native/swift/Sources/BlocksRuntime/KeychainCookieStore.swift
Problem
The Swift and Dart cookie jars keep only a cookie's name and value: the
Secure,PathandExpires/Max-Ageattributes are dropped, so every stored cookie for a host is sent on every request to it regardless of scheme or path, and expiry is honored only for a literalmax-age=0(Swift) or not at all (Dart, which also keys cookies by name only). This change makes both jars parse and enforce those attributes when storing and sending cookies, matching what the Kotlin cookie jar already does (#646).Issue #, if available: N/A
Changes
Rules applied on both platforms (same as Kotlin's
PersistentCookiesStorage/ KtorCookie.matches):Domainattribute is not used), only to request paths under itsPath(trailing-slash prefix match), and aSecurecookie only overhttps/wss.Pathapplies to the directory of the request path that set it (/fallback).Max-Agetakes precedence overExpires.Max-Age <= 0or a pastExpiresremoves the cookie; a stored cookie stops being sent once its expiry arrives.Swift (
native/swift)StoredCookie(parse, match, expiry, stored record) andCookieJar(storeSet-Cookie, buildCookieheader) over aCookieItemStoreprotocol, whichKeychainCookieStorenow conforms to.BlocksClient.executeandpostRawRouteuse the jar with the actual request URL.host|path|nameand holds a JSON record with the attributes. Items written by earlier versions (host|name→ bare value) are still read as before (Path=/, notSecure, no expiry), so an existing session keeps working after upgrade; the old item is removed the next time the server sets that cookie. Items written by this version are not readable by earlier versions.Dart (
native/dart/packages/blocks_runtime)SessionStoregainscookieHeaderFor(Uri)and an optionalrequestUrionsetCookies;BlocksClientandOidcClientpass the request URL to both.cookieHeaderandcookiesare unchanged in shape (cookieHeaderreturns every unexpired cookie). CustomSessionStoreimplementations need to add the new members.Expiresis parsed withoutdart:ioso the package stays web-compatible. TheSet-Cookiesplitter no longer splits at the comma inside anExpiresdate.PersistentSessionStorewrites a JSON list of cookie records. The name → value map written by earlier versions is still read (Path=/, noSecure, no expiry, any host) and replaced when the server sets the cookie again; an unreadable payload is cleared as before.Release notes
native/swift/.changeset/swift-cookie-attributes.md,minor(stored record format and cookie send behavior change).## Unreleasedinblocks_runtime/CHANGELOG.md(the Dart packages don't use changesets), marked breaking for theSessionStoreinterface additions.Validation
dart format --set-exit-if-changed,dart analyze(no new errors or warnings) anddart testinblocks_runtime— 96 tests pass, including new tests forSecuregating,Pathscoping and default path, host scoping,Max-Age/Expiresexpiry and precedence, joined headers withExpires, host/path keying, persistence round-trip of attributes, and reading/replacing the earlier storage format.blocks_codegentests also pass.dart test -p chromewas not run locally.swiftformat --lintandswiftlint lintover the package report no issues.StoredCookie.swift,CookieJar.swiftand the new cookie tests inBlocksClientTestswere compiled and run standalone with Swift 6.2.4 on Linux (16 tests pass). The full package (swift build/swift test/xcodebuildon the iOS simulator) depends on Apple frameworks and was not built locally — relying on CI for that.Checklist
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.