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 all Set-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 all Set-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.
TL;DR: Is there a recommended process for migrating a web app to issue cookies as
Partitionedwhich 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
Partitionedattribute 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 andwindow.openershenanigans.Our biggest problem is finding a way to safely roll-out the change to make cookies
Partitionedin 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 thePartitionedattribute.Naively making all new
Set-Cookieheaders have thePartitionedattribute would cause problems for users who have ongoing sessions at the point we apply the change, e.g.auth=some.session.dataPartitionedin allSet-CookieheadersSet-Cookie: auth=; Expires=Thu, 01-Jan-1970 00:00:00 GMT; PartitionedWe can work-around this specific case by, when issuing
Set-Cookieheader with anExpiresvalue in the past (i.e. when deleting a cookie), duplicate the header - once withPartitioned, and once without. This ensures that we delete the cookie no matter whether it was issued asPartitionedor not - although it does violate the recommendations of RFC 6265:However, we still have a problem when trying to update an existing cookie without deleting it, e.g.
auth=some.session.dataPartitionedin allSet-CookieheadersSet-Cookie: auth=renewed.session.data; PartitionedCookie: auth=some.session.data; auth=renewed.session.dataIn 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: