From 4df7a8605d516567b8f51ad881e6f0d6691a9d45 Mon Sep 17 00:00:00 2001 From: DCtheTall Date: Wed, 15 Sep 2021 15:52:21 -0400 Subject: [PATCH 1/8] Add support for partitioned cookies to CookieStore. --- index.bs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/index.bs b/index.bs index ebbf6fa..dd7f47b 100644 --- a/index.bs +++ b/index.bs @@ -500,12 +500,14 @@ dictionary CookieInit { USVString? domain = null; USVString path = "/"; CookieSameSite sameSite = "strict"; + boolean partitioned = false; }; dictionary CookieStoreDeleteOptions { required USVString name; USVString? domain = null; USVString path = "/"; + boolean? partitioned; }; dictionary CookieListItem { @@ -516,6 +518,7 @@ dictionary CookieListItem { DOMTimeStamp? expires; boolean secure; CookieSameSite sameSite; + boolean partitioned; }; typedef sequence CookieList; @@ -689,6 +692,9 @@ The set(|options|) method steps are: |options|["{{CookieInit/domain}}"], |options|["{{CookieInit/path}}"], and |options|["{{CookieInit/sameSite}}"]. + 1. If the |options|["{{CookieInit/partitioned}}"] attribute is present and the user agent supports [cookie partitioning](https://github.com/WICG/CHIPS), then + 1. If the current browsing context is a service worker, set the cookie's partition key be |origin|'s [=site=]. + 1. If the current browsing context is an HTML document, then the cookie's partition key is the [=site=] of the topmost frame. 1. If |r| is failure, then [=reject=] |p| with a {{TypeError}} and abort these steps. 1. [=/Resolve=] |p| with undefined. 1. Return |p|. @@ -743,6 +749,11 @@ The delete(|options|) method steps are: |options|["{{CookieStoreDeleteOptions/name}}"], |options|["{{CookieStoreDeleteOptions/domain}}"], and |options|["{{CookieStoreDeleteOptions/path}}"]. + 1. If the |options|["{{CookieStoreDeleteOptions/partitioned}}"] field is present and the user agent supports [cookie partitioning](https://github.com/WICG/CHIPS), then + 1. If the value is |false|, then |r| should only match cookies with no partition key. + 1. If the value is |true|, then + 1. If the browsing context is a service worker, then |r| should only match cookies whose partition key is the [=site=] of |origin|. + 1. If the browsing context is an HTML document, then |r| should only match cookies whose partition key is the [=site=] of the topmost frame. 1. If |r| is failure, then [=reject=] |p| with a {{TypeError}} and abort these steps. 1. [=/Resolve=] |p| with undefined. 1. Return |p|. @@ -1067,6 +1078,7 @@ To create a {{CookieListItem}} from |cookie|, run the following steps : \``Lax`\` :: Let |sameSite| be "{{CookieSameSite/lax}}". +1. Let |partitioned| be a boolean indicating that the user agent supports [cookie partitioning](https://github.com/WICG/CHIPS) and that that |cookie| has a partition key. 1. Return «[ "name" → |name|, "value" → |value|, @@ -1074,7 +1086,8 @@ To create a {{CookieListItem}} from |cookie|, run the following steps "path" → |path|, "expires" → |expires|, "secure" → |secure|, - "sameSite" → |sameSite| + "sameSite" → |sameSite|, + "partitioned" → |partitioned| ]» Note: The |cookie|'s From b25de9078072180b248f9497481e98be3e53ce6f Mon Sep 17 00:00:00 2001 From: DCtheTall Date: Wed, 15 Sep 2021 16:26:35 -0400 Subject: [PATCH 2/8] add examples --- explainer.md | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++ index.bs | 2 +- 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/explainer.md b/explainer.md index a59f595..fa56f78 100644 --- a/explainer.md +++ b/explainer.md @@ -14,9 +14,12 @@ + [Read a cookie](#read-a-cookie) + [Read multiple cookies](#read-multiple-cookies) + [Read the cookies for a specific URL](#read-the-cookies-for-a-specific-url) + + [Read a partitioned cookie](#read-a-partitioned-cookie) * [The Modifications API](#the-modifications-api) + [Write a cookie](#write-a-cookie) + + [Write a partitioned cookie](#write-a-partitioned-cookie) + [Delete a cookie](#delete-a-cookie) + + [Delete a partitioned cookie](#delete-a-partitioned-cookie) + [Access all the cookie data](#access-all-the-cookie-data) * [The Change Events API](#the-change-events-api) + [Get change events in documents](#get-change-events-in-documents) @@ -263,6 +266,22 @@ any URL under their scope. Documents can only obtain the cookies at their current URL. In other words, the only valid `url` value in Document contexts is the document's URL. +### Read a partitioned cookie + +If the user agent supports +[cookie partitioning](https://github.com/DCtheTall/CHIPS), then the cookie +objects will have a boolean value indicating if the cookie is partitioned. + +```javascript +// Read a cookie set without the Partitioned attribute. +const cookie = await cookieStore.get('session_id'); +console.log(cookie.partitioned); // -> false + +// Read a Partitioned cookie from a third-party context. +const cookie = await cookieStore.get('__Host-third_party_session_id'); +console.log(cookie.partitioned); // -> true +``` + ## The Modifications API Both documents and service workers access the same modification API, via the @@ -292,6 +311,21 @@ await cookieStore.set({ }); ``` +### Write a partitioned cookie + +If the user agent supports [cookie partitioning](https://github.com/WICG/CHIPS) +then you can set a partitioned cookie in a third-party context using the following. + +```javascript +await cookieStore.set({ + name: '__Host-third_party_session_id', + value: 'foobar', + path: '/', + sameSite: 'none' + // `Secure` is implicitly set +}); +``` + ### Delete a cookie ```javascript @@ -316,6 +350,27 @@ try { } ``` +### Delete a partitioned cookie + +If the user agent supports [cookie partitioning](https://github.com/WICG/CHIPS) +then it is possible for a site to set both a partitioned and unpartitioned +cookie with the same name. + +In this edge case, if a site would like to distinguish between whether they +want to delete their partitioned and unpartitioned cookie, they can provide +a `partitioned` attribute. If the site wants to delete the partitioned cookie, +the site could use: + +```javascript +await cookieStore.delete({ + name: '__Host-third_party_session_id', + partitioned: true +}); +``` + +If the site wants to delete the unpartitioned cookie, change the `partitioned` +field to `false`. + ### Access all the cookie data The objects returned by `get` and `getAll` contain all the information in the diff --git a/index.bs b/index.bs index dd7f47b..3886097 100644 --- a/index.bs +++ b/index.bs @@ -507,7 +507,7 @@ dictionary CookieStoreDeleteOptions { required USVString name; USVString? domain = null; USVString path = "/"; - boolean? partitioned; + boolean partitioned; }; dictionary CookieListItem { From 625fb5158c984a4bdda370b01aae232b111b7752 Mon Sep 17 00:00:00 2001 From: DCtheTall Date: Thu, 7 Oct 2021 17:39:37 -0400 Subject: [PATCH 3/8] add default value to delete --- explainer.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/explainer.md b/explainer.md index fa56f78..56a1db7 100644 --- a/explainer.md +++ b/explainer.md @@ -369,7 +369,7 @@ await cookieStore.delete({ ``` If the site wants to delete the unpartitioned cookie, change the `partitioned` -field to `false`. +field to `false`. If the field is not present, the value defaults to `false`. ### Access all the cookie data From 976425a950233dd8ef1ee6278aaf531e0d135659 Mon Sep 17 00:00:00 2001 From: DCtheTall Date: Wed, 3 Nov 2021 09:37:59 -0400 Subject: [PATCH 4/8] Add partitioned to set and get --- explainer.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/explainer.md b/explainer.md index 56a1db7..524f303 100644 --- a/explainer.md +++ b/explainer.md @@ -278,7 +278,10 @@ const cookie = await cookieStore.get('session_id'); console.log(cookie.partitioned); // -> false // Read a Partitioned cookie from a third-party context. -const cookie = await cookieStore.get('__Host-third_party_session_id'); +const cookie = await cookieStore.get({ + name: '__Host-third_party_session_id', + partitioned: true +}); console.log(cookie.partitioned); // -> true ``` @@ -321,7 +324,8 @@ await cookieStore.set({ name: '__Host-third_party_session_id', value: 'foobar', path: '/', - sameSite: 'none' + sameSite: 'none', + partitioned: true // `Secure` is implicitly set }); ``` From aa12ba36da234c458a00ab795351945c55f05a43 Mon Sep 17 00:00:00 2001 From: DCtheTall Date: Wed, 19 Jul 2023 13:41:12 -0400 Subject: [PATCH 5/8] review comments --- explainer.md | 13 ++++--------- index.bs | 10 +++++----- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/explainer.md b/explainer.md index 524f303..407827f 100644 --- a/explainer.md +++ b/explainer.md @@ -268,9 +268,7 @@ the only valid `url` value in Document contexts is the document's URL. ### Read a partitioned cookie -If the user agent supports -[cookie partitioning](https://github.com/DCtheTall/CHIPS), then the cookie -objects will have a boolean value indicating if the cookie is partitioned. +The cookie objects will have a boolean value indicating if the cookie is partitioned. ```javascript // Read a cookie set without the Partitioned attribute. @@ -360,10 +358,7 @@ If the user agent supports [cookie partitioning](https://github.com/WICG/CHIPS) then it is possible for a site to set both a partitioned and unpartitioned cookie with the same name. -In this edge case, if a site would like to distinguish between whether they -want to delete their partitioned and unpartitioned cookie, they can provide -a `partitioned` attribute. If the site wants to delete the partitioned cookie, -the site could use: +To delete a partitioned cookie, the `partitioned` parameter must be provided: ```javascript await cookieStore.delete({ @@ -372,8 +367,8 @@ await cookieStore.delete({ }); ``` -If the site wants to delete the unpartitioned cookie, change the `partitioned` -field to `false`. If the field is not present, the value defaults to `false`. +If the site wants to only delete the unpartitioned cookie, change the `partitioned` +field to `false` or omit the property. ### Access all the cookie data diff --git a/index.bs b/index.bs index 4ea8cd4..3582e63 100644 --- a/index.bs +++ b/index.bs @@ -520,7 +520,7 @@ dictionary CookieStoreDeleteOptions { required USVString name; USVString? domain = null; USVString path = "/"; - boolean partitioned; + boolean partitioned = false; }; dictionary CookieListItem { @@ -711,7 +711,7 @@ The set(|options|) method steps are: |options|["{{CookieInit/domain}}"], |options|["{{CookieInit/path}}"], and |options|["{{CookieInit/sameSite}}"]. - 1. If the |options|["{{CookieInit/partitioned}}"] attribute is present and the user agent supports [cookie partitioning](https://github.com/WICG/CHIPS), then + 1. If |options|["{{CookieInit/partitioned}}"] is present and the user agent supports [cookie partitioning](https://github.com/privacycg/CHIPS), then 1. If the current browsing context is a service worker, set the cookie's partition key be |origin|'s [=site=]. 1. If the current browsing context is an HTML document, then the cookie's partition key is the [=site=] of the topmost frame. 1. If |r| is failure, then [=reject=] |p| with a {{TypeError}} and abort these steps. @@ -770,11 +770,11 @@ The delete(|options|) method steps are: |options|["{{CookieStoreDeleteOptions/name}}"], |options|["{{CookieStoreDeleteOptions/domain}}"], and |options|["{{CookieStoreDeleteOptions/path}}"]. - 1. If the |options|["{{CookieStoreDeleteOptions/partitioned}}"] field is present and the user agent supports [cookie partitioning](https://github.com/WICG/CHIPS), then - 1. If the value is |false|, then |r| should only match cookies with no partition key. + 1. If |options|["{{CookieStoreDeleteOptions/partitioned}}"] is present and the user agent supports [cookie partitioning](https://github.com/privacycg/CHIPS), then 1. If the value is |true|, then 1. If the browsing context is a service worker, then |r| should only match cookies whose partition key is the [=site=] of |origin|. 1. If the browsing context is an HTML document, then |r| should only match cookies whose partition key is the [=site=] of the topmost frame. + 1. Otherwise |r| should only match cookies with no partition key. 1. If |r| is failure, then [=reject=] |p| with a {{TypeError}} and abort these steps. 1. [=/Resolve=] |p| with undefined. 1. Return |p|. @@ -1095,7 +1095,7 @@ To create a {{CookieListItem}} from |cookie|, run the following steps : \``Lax`\` :: Let |sameSite| be "{{CookieSameSite/lax}}". -1. Let |partitioned| be a boolean indicating that the user agent supports [cookie partitioning](https://github.com/WICG/CHIPS) and that that |cookie| has a partition key. +1. Let |partitioned| be a boolean indicating that the user agent supports [cookie partitioning](https://github.com/privacycg/CHIPS) and that that |cookie| has a partition key. 1. Return «[ "name" → |name|, "value" → |value|, From b4504f9870ea2b61b0023db7ddaec378ae4e27a4 Mon Sep 17 00:00:00 2001 From: DCtheTall Date: Wed, 19 Jul 2023 13:52:11 -0400 Subject: [PATCH 6/8] Modify set cookie algo for partitioned --- index.bs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/index.bs b/index.bs index 3582e63..f7582fd 100644 --- a/index.bs +++ b/index.bs @@ -701,6 +701,8 @@ The set(|options|) method steps are: 1. Let |origin| be |settings|'s [=environment settings object/origin=]. 1. If |origin| is an [=opaque origin=], then return [=a promise rejected with=] a "{{SecurityError}}" {{DOMException}}. 1. Let |url| be |settings|'s [=environment/creation URL=]. +1. Let |partitioned| be a boolean that is true iff |options|["{{CookieInit/partitioned}}"] is present and set to true. + 1. Otherwise set |partitioned| to false. 1. Let |p| be [=a new promise=]. 1. Run the following steps [=in parallel=]: 1. Let |r| be the result of running [=set a cookie=] with @@ -709,11 +711,9 @@ The set(|options|) method steps are: |options|["{{CookieInit/value}}"], |options|["{{CookieInit/expires}}"], |options|["{{CookieInit/domain}}"], - |options|["{{CookieInit/path}}"], and - |options|["{{CookieInit/sameSite}}"]. - 1. If |options|["{{CookieInit/partitioned}}"] is present and the user agent supports [cookie partitioning](https://github.com/privacycg/CHIPS), then - 1. If the current browsing context is a service worker, set the cookie's partition key be |origin|'s [=site=]. - 1. If the current browsing context is an HTML document, then the cookie's partition key is the [=site=] of the topmost frame. + |options|["{{CookieInit/path}}"], + |options|["{{CookieInit/sameSite}}"], and + |partitioned|. 1. If |r| is failure, then [=reject=] |p| with a {{TypeError}} and abort these steps. 1. [=/Resolve=] |p| with undefined. 1. Return |p|. @@ -1129,8 +1129,9 @@ To set a cookie with |value|, optional |expires|, |domain|, -|path|, and -|sameSite|, +|path|, +|sameSite|, and +|partitioned| run the following steps: 1. If |name| or |value| contain U+003B (`;`), any [=C0 control=] character except U+0009 (the horizontal tab character), or U+007F, then return failure. @@ -1169,6 +1170,7 @@ run the following steps: : "{{CookieSameSite/lax}}" :: [=list/Append=] \``SameSite`\`/\``Lax`\` to |attributes|. +1. If |partitioned| is true, [=list/Append=] \``Partitioned`\`/\`\` to |attributes|. 1. Perform the steps defined in [[RFC6265bis#section-5.6]] for when the user agent "receives a cookie" with |url| as request-uri, |encodedName| as cookie-name, From 023a7cf1241737ba08103f703f2c2c027622dd09 Mon Sep 17 00:00:00 2001 From: DCtheTall Date: Mon, 31 Jul 2023 12:46:07 -0400 Subject: [PATCH 7/8] Review comments --- index.bs | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/index.bs b/index.bs index f7582fd..fca9dab 100644 --- a/index.bs +++ b/index.bs @@ -701,8 +701,7 @@ The set(|options|) method steps are: 1. Let |origin| be |settings|'s [=environment settings object/origin=]. 1. If |origin| is an [=opaque origin=], then return [=a promise rejected with=] a "{{SecurityError}}" {{DOMException}}. 1. Let |url| be |settings|'s [=environment/creation URL=]. -1. Let |partitioned| be a boolean that is true iff |options|["{{CookieInit/partitioned}}"] is present and set to true. - 1. Otherwise set |partitioned| to false. +1. Let |partitioned| be true if |options|["{{CookieInit/partitioned}}"] is present and true, or false otherwise. 1. Let |p| be [=a new promise=]. 1. Run the following steps [=in parallel=]: 1. Let |r| be the result of running [=set a cookie=] with @@ -768,13 +767,9 @@ The delete(|options|) method steps are: 1. Let |r| be the result of running [=delete a cookie=] with |url|, |options|["{{CookieStoreDeleteOptions/name}}"], - |options|["{{CookieStoreDeleteOptions/domain}}"], and - |options|["{{CookieStoreDeleteOptions/path}}"]. - 1. If |options|["{{CookieStoreDeleteOptions/partitioned}}"] is present and the user agent supports [cookie partitioning](https://github.com/privacycg/CHIPS), then - 1. If the value is |true|, then - 1. If the browsing context is a service worker, then |r| should only match cookies whose partition key is the [=site=] of |origin|. - 1. If the browsing context is an HTML document, then |r| should only match cookies whose partition key is the [=site=] of the topmost frame. - 1. Otherwise |r| should only match cookies with no partition key. + |options|["{{CookieStoreDeleteOptions/domain}}"], + |options|["{{CookieStoreDeleteOptions/path}}"], + |options|["{{CookieStoreDeleteOptions/partitioned}}"] 1. If |r| is failure, then [=reject=] |p| with a {{TypeError}} and abort these steps. 1. [=/Resolve=] |p| with undefined. 1. Return |p|. @@ -1195,8 +1190,9 @@ run the following steps: To delete a cookie with |url|, |name|, -|domain| and -|path|, +|domain|, +|path|, and +|partitioned| run the following steps: 1. If |path| is not null, then run these steps: @@ -1220,8 +1216,9 @@ run the following steps: |value|, |expires|, |domain|, - |path|, and - |sameSite|. + |path|, + |sameSite|, and + |partitioned|. From 28de9d06696bdd41b7950a7c4667a88efe84acf3 Mon Sep 17 00:00:00 2001 From: DCtheTall Date: Fri, 4 Aug 2023 17:28:35 -0400 Subject: [PATCH 8/8] review comments --- index.bs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/index.bs b/index.bs index fca9dab..70c153a 100644 --- a/index.bs +++ b/index.bs @@ -701,7 +701,6 @@ The set(|options|) method steps are: 1. Let |origin| be |settings|'s [=environment settings object/origin=]. 1. If |origin| is an [=opaque origin=], then return [=a promise rejected with=] a "{{SecurityError}}" {{DOMException}}. 1. Let |url| be |settings|'s [=environment/creation URL=]. -1. Let |partitioned| be true if |options|["{{CookieInit/partitioned}}"] is present and true, or false otherwise. 1. Let |p| be [=a new promise=]. 1. Run the following steps [=in parallel=]: 1. Let |r| be the result of running [=set a cookie=] with @@ -712,7 +711,7 @@ The set(|options|) method steps are: |options|["{{CookieInit/domain}}"], |options|["{{CookieInit/path}}"], |options|["{{CookieInit/sameSite}}"], and - |partitioned|. + |options|["{{CookieInit/partitioned}}"]. 1. If |r| is failure, then [=reject=] |p| with a {{TypeError}} and abort these steps. 1. [=/Resolve=] |p| with undefined. 1. Return |p|. @@ -768,8 +767,8 @@ The delete(|options|) method steps are: |url|, |options|["{{CookieStoreDeleteOptions/name}}"], |options|["{{CookieStoreDeleteOptions/domain}}"], - |options|["{{CookieStoreDeleteOptions/path}}"], - |options|["{{CookieStoreDeleteOptions/partitioned}}"] + |options|["{{CookieStoreDeleteOptions/path}}"], and + |options|["{{CookieStoreDeleteOptions/partitioned}}"]. 1. If |r| is failure, then [=reject=] |p| with a {{TypeError}} and abort these steps. 1. [=/Resolve=] |p| with undefined. 1. Return |p|.