Skip to content

Integrate with new draft cookie spec (draft-annevk-johannhof-httpbis-cookies/00+ε) #1807

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
221 changes: 180 additions & 41 deletions fetch.bs
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,29 @@ urlPrefix:https://tc39.es/ecma262/#;type:dfn;spec:ecma-262
url:realm;text:realm
url:sec-list-and-record-specification-type;text:Record
url:current-realm;text:current realm

urlPrefix:https://www.ietf.org/archive/id/draft-annevk-johannhof-httpbis-cookies-00.html#;type:dfn;spec:cookies
url:name-cookie-store-and-limits;text:cookie store
url:name-parse-and-store-a-cookie;text:parse and store a cookie
url:name-parse-a-cookie;text:parse a cookie
url:name-store-a-cookie;text:store a cookie
url:name-retrieve-cookies;text:retrieve cookies
url:name-serialize-cookies;text:serialize cookies
url:name-garbage-collect-cookies;text:garbage collect cookies

<!-- TODO: pending HTML changes- ancestor enum (https://github.com/whatwg/html/pull/10559), has storage access bit, initiator origin plumbing -->
urlPrefix:https://html.spec.whatwg.org#;type:dfn;spec:html
url:TODO;text:ancestry;for:environment
url:TODO;text:has storage access;for:environment
</pre>

<pre class=biblio>
{
"COOKIES": {
"authors": ["Johann Hofmann", "Anne van Kesteren"],
"href": "https://www.ietf.org/archive/id/draft-annevk-johannhof-httpbis-cookies-00.html",
"title": "Cookies: HTTP State Management Mechanism"
},
"HTTP": {
"aliasOf": "RFC9110"
},
Expand Down Expand Up @@ -1938,6 +1957,10 @@ not always relevant and might require different behavior.
"<code>client</code>" or an <a for=/>origin</a>. Unless stated otherwise it is
"<code>client</code>".

<p>A <a for=/>request</a> has an associated
<dfn export for=request id=concept-request-navigation-initiator-origin>top-level navigation
initiator origin</dfn>, which is an <a for=/>origin</a> or null. Unless stated otherwise it is null.

<p class=note>"<code>client</code>" is changed to an <a for=/>origin</a> during
<a lt=fetch for=/>fetching</a>. It provides a convenient way for standards to not have to set
<a for=/>request</a>'s <a for=request>origin</a>.
Expand Down Expand Up @@ -2226,31 +2249,39 @@ or "<code>object</code>".
<hr>

<div algorithm>
<p>A <a for=/>request</a> <var>request</var> has a
<dfn for=request id=concept-request-tainted-origin>redirect-tainted origin</dfn> if these steps
return true:
<p>To compute the <dfn for=request id=concept-request-redirect-taint>redirect-taint</dfn> of a
<a for=/>request</a> <var>request</var>, perform the following steps. They return
"<code>same-origin</code>", "<code>same-site</code>", or "<code>cross-site</code>".

<ol>
<li><p><a for=/>Assert</a>: <var>request</var>'s <a for=request>origin</a> is not
"<code>client</code>".

<li><p>Let <var>lastURL</var> be null.

<li><p>Let <var>computedTaint</var> be "<code>same-origin</code>".

<li>
<p><a for=list>For each</a> <var>url</var> of <var>request</var>'s <a for=request>URL list</a>:

<ol>
<li><p>If <var>lastURL</var> is null, then set <var>lastURL</var> to <var>url</var> and
<a for=iteration>continue</a>.

<li><p>If <var>url</var>'s <a for=url>origin</a> is not <a for=/>same site</a> with
<var>lastURL</var>'s <a for=url>origin</a> and <var>request</var>'s <a for=request>origin</a> is
not <a for=/>same site</a> with <var>lastURL</var>'s <a for=url>origin</a>, then return
"<code>cross-site</code>".

<li><p>If <var>url</var>'s <a for=url>origin</a> is not <a>same origin</a> with
<var>lastURL</var>'s <a for=url>origin</a> and <var>request</var>'s <a for=request>origin</a> is
not <a>same origin</a> with <var>lastURL</var>'s <a for=url>origin</a>, then return true.
not <a>same origin</a> with <var>lastURL</var>'s <a for=url>origin</a>, then set
<var>computedTaint</var> to "<code>same-site</code>".

<li>Set <var>lastURL</var> to <var>url</var>.
</ol>

<li>Return false.
<li><p>Return <var>computedTaint</var>.
</ol>
</div>

Expand All @@ -2262,8 +2293,8 @@ run these steps:
<li><p><a for=/>Assert</a>: <var>request</var>'s <a for=request>origin</a> is not
"<code>client</code>".

<li><p>If <var>request</var> has a <a for=request>redirect-tainted origin</a>, then return
"<code>null</code>".
<li><p>If <var>request</var>'s <a for=request>redirect-taint</a> is not "<code>same-origin</code>",
then return "<code>null</code>".

<li><p>Return <var>request</var>'s <a for=request>origin</a>,
<a lt="ASCII serialization of an origin">serialized</a>.
Expand Down Expand Up @@ -2362,20 +2393,20 @@ source of security bugs. Please seek security review for features that deal with
"<code>client</code>".

<li><p>If <var>request</var>'s <a for=request>mode</a> is not "<code>no-cors</code>", then return
true.</p>
true.

<li><p>If <var>request</var>'s <a for=request>client</a> is null, then return true.</p>
<li><p>If <var>request</var>'s <a for=request>client</a> is null, then return true.

<li><p>If <var>request</var>'s <a for=request>client</a>'s
<a for="environment settings object">policy container</a>'s
<a for="policy container">embedder policy</a>'s <a for="embedder policy">value</a> is not
"<a for="embedder policy value"><code>credentialless</code></a>", then return true.</p>
"<a for="embedder policy value"><code>credentialless</code></a>", then return true.

<li><p>If <var>request</var>'s <a for=request>origin</a> is <a>same origin</a> with
<var>request</var>'s <a for=request>current URL</a>'s <a for=url>origin</a> and <var>request</var>
does not have a <a for=request>redirect-tainted origin</a>, then return true.</p>
<var>request</var>'s <a for=request>current URL</a>'s <a for=url>origin</a> and <var>request</var>'s
<a for=request>redirect-taint</a> is not "<code>same-origin</code>", then return true.

<li><p>Return false.</p>
<li><p>Return false.
</ol>
</div>

Expand Down Expand Up @@ -2486,8 +2517,9 @@ this is also tracked internally using the request's <a for=request>timing allow
<dfn export for=response>service worker timing info</dfn> (null or a
<a for=/>service worker timing info</a>), which is initially null.

<p>A <a for=/>response</a> has an associated <dfn for=response>has-cross-origin-redirects</dfn>
(a boolean), which is initially false.
<p>A <a for=/>response</a> has an associated <dfn for=response>redirect taint</dfn>
("<code>same-origin</code>", "<code>same-site</code>", or "<code>cross-site</code>"), which is
initially "<code>same-origin</code>".

<hr>

Expand Down Expand Up @@ -4225,7 +4257,133 @@ indicates the request’s purpose is to fetch a resource that is anticipated to
<p class=note>The server can use this to adjust the caching expiry for prefetches, to disallow the
prefetch, or to treat it differently when counting page visits.

<h2 id=cookies>Cookies</h2>

<h3 id=cookie-header>`<code>Cookie</code>` header</h3>

<p>The `<code>Cookie</code>` header is largely defined in its own specification. [[COOKIES]].
We define infrastructure to be able to use them conveniently here.

<div algorithm>
<p>To <dfn id=append-a-request-cookie-header>append a request `<code>Cookie</code>` header</dfn>,
given a <a for=/>request</a> <var>request</var>, run these steps:

<ol>
<li><p>If the user agent is configured to disable cookies for <var>request</var>, it should
return.

<li><p>Let |sameSite| be the result of [=determining the same-site mode=] for <var>request</var>.

<li><p>Let |isSecure| be false.

<li><p>If <var>request</var>'s <a for=request>client</a> is a <a>secure context</a>, then set
|isSecure| to true.

<li>
<p>Let |httpOnlyAllowed| be true.

<p class=note>True follows from this being invoked from <a>fetch</a>, as opposed to the
<code>document.cookie</code> getter steps for instance.

<li>
<p>Let |cookies| be the result of running <a>retrieve cookies</a> given |isSecure|,
<var>request</var>'s <a for=request>current URL</a>'s <a for=url>host</a>, <var>request</var>'s
<a for=request>current URL</a>'s <a for=url>path</a>, |httpOnlyAllowed|, and |sameSite|.

<p class=note>It is expected that the cookie store returns an ordered list of cookies

<li>If |cookies| <a for="list">is empty</a>, then return.

<li>Let |value| be the result of running <a>serialize cookies</a> given |cookies|.

<li><a for="header list">Append</a> (`<code>Cookie</code>`, <var>value</var>) to
<var>request</var>'s <a for=request>header list</a>.
</ol>
</div>

<div algorithm>
<p>To <dfn id=parse-and-store-response-cookie-headers>parse and store response
`<code>Set-Cookie</code>` headers</dfn>, given a <a for=/>request</a> <var>request</var> and a
<a for=/>response</a> <var>response</var>, run these steps:

<ol>
<li><p>If the user agent is configured to disable cookies for <var>request</var>,
then it should return.

<li><p>Let |allowNonHostOnlyCookieForPublicSuffix| be false.

<li><p>Let |isSecure| be false.

<li><p>If <var>request</var>'s <a for=request>client</a> is a <a>secure context</a>, set
|isSecure| to true.

<li>
<p>Let |httpOnlyAllowed| be true.

<p class=note>True follows from this being invoked from <a>fetch</a>, as opposed to the
<code>document.cookie</code> getter steps for instance.

<li><p>Let |sameSiteStrictOrLaxAllowed| be true if the result of [=determine the same-site mode=]
for |request| is "<code>StrictOrLess</code>", and false otherwise.

<li><p><a for=list>For each</a> <var>header</var> of <var>response</var>'s
<a for=response>header list</a>:

<ol>
<li><p>If <var>header</var>'s <a for=header>name</a> is not a <a>byte-case-insensitive</a> match
for `<code>Set-Cookie</code>`, <a for=iteration>continue</a>.

<li><p><a>Parse and store a cookie</a> given <var>header</var>'s <a for=header>value</a>,
|isSecure|, <var>request</var>'s <a for=request>current URL</a>'s <a for=url>host</a>,
<var>request</var>'s <a for=request>current URL</a>'s <a for=url>path</a>, |httpOnlyAllowed|,
|allowNonHostOnlyCookieForPublicSuffix|, and |sameSiteStrictOrLaxAllowed|.

<li><p><a>Garbage collect cookies</a> given <var>request</var>'s
<a for=request>current URL</a>'s <a for=url>host</a>.
</ol>
</ol>
</div>

<h3 id=cookie-infrastructure>Cookie Infrastructure</h3>

These algorithms are not only for use with the `<code>Cookie</code>` header, and are used in other
specifications.

<div algorithm>
<p>To <dfn>determine the same-site mode</dfn> for a given <a for=/>request</a> <var>request</var>,
run these steps:

<ol>
<li><p><a for=/>Assert</a>: <var>request</var>'s <a for=request>method</a> is "GET" or "POST".

<li><p>If <var>request</var>'s <a for=request>top-level navigation initiator origin</a> is not
null and is not <a for=/>same site</a> to <var>request</var>'s <a for=request>URL</a>'s
<a for=url>origin</a>, return "<code>UnsetOrLess</code>".

<li><p>If <var>request</var>'s <a for=request>method</a> is "GET" and <var>request</var>'s
<a for=request>destination</a> is "document", return "<code>LaxOrLess</code>".

<li><p>If <var>request</var>'s <a for=request>client</a>'s <a for=environment>ancestry</a> is
"<code>cross-site</code>", return "<code>UnsetOrLess</code>".

<li><p>If <var>request</var>'s <a for=request>redirect-taint</a> is "<code>cross-site</code>",
return "<code>UnsetOrLess</code>".

<li><p>Return "<code>StrictOrLess</code>".
</ol>
</div>

<div algorithm>
<p> To <dfn export>clear site cookies</dfn> for <a for=/>origin</a> <var>origin</var>, run these steps:

<ol>
<li><p>Remove all cookies that were stored with a host is either equal to |origin|'s <a for=url>host</a>,
or have a <a>registrable domain</a> equal to |origin|'s <a for=url>host</a>'s <a>registrable domain</a>.

<p class=XXX>The cookie specification doesn't yet have a way to do this algorithmically, so we
settle for this definition.
</ol>
</div>

<h2 id=fetching>Fetching</h2>

Expand Down Expand Up @@ -4680,8 +4838,8 @@ steps:
<!-- If you are ever tempted to move this around, carefully consider responses from about URLs,
blob URLs, service workers, HTTP cache, HTTP network, etc. -->

<li><p>If <var>request</var> has a <a for=request>redirect-tainted origin</a>, then set
<var>internalResponse</var>'s <a for=response>has-cross-origin-redirects</a> to true.
<li><p>Set <var>internalResponse</var>'s <a for=response>redirect taint</a> to <var>request</var>'s
<a for=request>redirect-taint</a>.

<li><p>If <var>request</var>'s <a for=request>timing allow failed flag</a> is unset, then set
<var>internalResponse</var>'s <a for=response>timing allow passed flag</a>.
Expand Down Expand Up @@ -4834,7 +4992,7 @@ steps:
<li>
<p>If <var>fetchParams</var>'s <a for="fetch params">request</a>'s <a for=request>mode</a> is
not "<code>navigate</code>" or <var>response</var>'s
<a for=response>has-cross-origin-redirects</a> is false:
<a for=response>redirect taint</a> is "<code>same-origin</code>":

<ol>
<li><p>Set <var>responseStatus</var> to <var>response</var>'s <a for=response>status</a>.
Expand Down Expand Up @@ -5710,21 +5868,7 @@ run these steps:
<p>If <var>includeCredentials</var> is true, then:

<ol>
<li>
<p>If the user agent is not configured to block cookies for <var>httpRequest</var> (see
<a href=https://httpwg.org/specs/rfc6265.html#privacy-considerations>section 7</a> of
[[!COOKIES]]), then:

<ol>
<li><p>Let <var>cookies</var> be the result of running the "cookie-string" algorithm (see
<a href=https://httpwg.org/specs/rfc6265.html#cookie>section 5.4</a> of
[[!COOKIES]]) with the user agent's cookie store and <var>httpRequest</var>'s
<a for=request>current URL</a>.

<li>If <var>cookies</var> is not the empty string, then <a for="header list">append</a>
(`<code>Cookie</code>`, <var>cookies</var>) to <var>httpRequest</var>'s
<a for=request>header list</a>.
</ol>
<li><p><a>Append a request `<code>Cookie</code>` header</a> for <var>httpRequest</var>.

<li>
<p>If <var>httpRequest</var>'s <a for=request>header list</a>
Expand Down Expand Up @@ -6288,14 +6432,9 @@ optional boolean <var>forceNewConnection</var> (default false), run these steps:
<li><p>Set <var>response</var>'s <a for=response>body</a> to a new <a for=/>body</a> whose
<a for=body>stream</a> is <var>stream</var>.

<li><p tracking-vector>If <var>includeCredentials</var> is true and the user agent is not
configured to block cookies for <var>request</var> (see
<a href=https://httpwg.org/specs/rfc6265.html#privacy-considerations>section 7</a> of
[[!COOKIES]]), then run the "set-cookie-string" parsing algorithm (see
<a href=https://httpwg.org/specs/rfc6265.html#set-cookie>section 5.2</a> of [[!COOKIES]]) on the
<a for=header>value</a> of each <a for=/>header</a> whose <a for=header>name</a> is a
<a>byte-case-insensitive</a> match for `<code>Set-Cookie</code>` in <var>response</var>'s
<a for=response>header list</a>, if any, and <var>request</var>'s <a for=request>current URL</a>.
<li><p tracking-vector>If <var>includeCredentials</var> is true, the user agent should
<a>parse and store response `<code>Set-Cookie</code>` headers</a> given <var>request</var> and
<var>response</var>.

<li>
<p>Run these steps <a>in parallel</a>:
Expand Down