-
Notifications
You must be signed in to change notification settings - Fork 38
Description
TL;DR: Is there a recommended process for migrating a web app to issue cookies as Partitioned
which does not break ongoing cookies/sessions?
We currently have a big, old, complex, monolithic web app which uses cookies mostly for authentication. Our app is sometimes used in a cross-origin iframe e.g. embedded in a CRM, and sometimes used in a top-level context (and sometimes both, in the same browser session).
To avoid being broken by third-party cookie blocking, we are intending to add the Partitioned
attribute to all cookies issued by our app. Being unable to share cookies between the iframe and top-level context is a bit of a problem, but we're mostly working-around it with some (unfortunately slightly hacky) popups and window.opener
shenanigans.
Our biggest problem is finding a way to safely roll-out the change to make cookies Partitioned
in a backward-compatible manner (and forward-compatible, in case we need to temporarily roll-back the change), which we need to do before browsers actually start blocking third-party cookies. Some of our enterprise customers have quite slow-moving browser update policies, which means our solution should ideally still work for browsers that don't understand/respect the Partitioned
attribute.
Naively making all new Set-Cookie
headers have the Partitioned
attribute would cause problems for users who have ongoing sessions at the point we apply the change, e.g.
- A user has an unpartitioned cookie
auth=some.session.data
- We update our app to include
Partitioned
in allSet-Cookie
headers - The user tries to log out, causing the server to send a response header like
Set-Cookie: auth=; Expires=Thu, 01-Jan-1970 00:00:00 GMT; Partitioned
- Because the existing cookie in the browser is unpartitioned, the browser does not delete the cookie, so the user remains logged in despite their best efforts
We can work-around this specific case by, when issuing Set-Cookie
header with an Expires
value in the past (i.e. when deleting a cookie), duplicate the header - once with Partitioned
, and once without. This ensures that we delete the cookie no matter whether it was issued as Partitioned
or not - although it does violate the recommendations of RFC 6265:
Servers SHOULD NOT include more than one Set-Cookie header field in the same response with the same cookie-name.
However, we still have a problem when trying to update an existing cookie without deleting it, e.g.
- A user has an unpartitioned cookie
auth=some.session.data
- We update our app to include
Partitioned
in allSet-Cookie
headers - The user's session data changes (e.g. when it is renewed) causing the server to send a response header like
Set-Cookie: auth=renewed.session.data; Partitioned
- Because the existing cookie in the browser is unpartitioned, the browser does not replace the existing cookie - it instead adds a new cookie with the same name
- Subsequent requests from the browser include both cookie values in the request header, e.g.
Cookie: auth=some.session.data; auth=renewed.session.data
In this case, we would generally want the server to always use the most recently-set cookie value. But with the current browser behaviour, we would need to rely on this always being the last value for the cookie name - this is also against RFC 6265:
Although cookies are serialized linearly in the Cookie header, servers SHOULD NOT rely upon the serialization order. In particular, if the Cookie header contains two cookies with the same name (e.g., that were set with different Path or Domain attributes), servers SHOULD NOT rely upon the order in which these cookies appear in the header.