wicg / cookie-store Goto Github PK
View Code? Open in Web Editor NEWAsynchronous access to cookies from JavaScript
Home Page: https://wicg.github.io/cookie-store/
License: Apache License 2.0
Asynchronous access to cookies from JavaScript
Home Page: https://wicg.github.io/cookie-store/
License: Apache License 2.0
Developers may be tempted to use cookies as a key/value store for their applications. The explainer should describe the limitations of such an approach and refer developers to other storage APIs for situations where cookies are inappropriate or suboptimal, perhaps with short code samples to ease the transition
https://wicg.github.io/cookie-store/ appears to just be a copy of the readme.
It's very hard to evaluate how to implement this API without having this algorithm specified.
I can easily guess the meaning of 'name' and 'matchType', but what's the meaning of 'url' when this API is used on window.cookieStore? Is it 'similar' to 'path' or should it be ignored?
Existing server-side session cookie systems sometimes use HttpOnly cookies to avoid XSS-driven session cookie stealing. However, these single-cookie sessions (often implemented by server-side web app frameworks) are not easily portable to offline apps using ServiceWorker, because the ServiceWorker won't be able to read the cookie to perform a session check.
What if the server could opt the service worker script in to reading HttpOnly cookies by including a special response header in the worker script response, e.g. Service-Worker-HttpOnly-Cookies-Allowed: true
?
If we did this, we would also need a boolean in cookie-reading and cookie-monitoring APIs to determine whether a given operation reveals such cookies, e.g. cookieStore.get('COOKIENAME', {includeHttpOnly: true}
What do you think, @metromoxie @annevk @mikewest ?
Another intermediate option would be to allow the service worker to see one of the following:
Also, returned cookie objects could indicate their httpOnly status alongside name and value
Apparently document.cookie
supports cookies with no value via document.cookie = "foo"
. This is distinct on the wire (i.e. in the resulting HTTP headers) from cookies with value empty string (document.cookie = "foo="
).
Is support for this needed, so that this API encompasses the entire cookie data model, or can we be stricter?
Related: whatwg/html#804
When creating pull requests, it's really beneficial to have a preview of the PR showing the actual version and also a diff showing the changes.
This repo has the json file to enable it but the preview isn't working.
interface Cookie {
readonly DOMString name;
readonly DOMString value;
readonly DOMString path;
};
interface CookieList {
maplike<DOMString, sequence<Cookie>>;
Cookie get(DOMString name);
squence<Cookie> getAll(DOMString name);
};
dictionary CookieOptions {
DOMString path;
(DOMString or Date) expiration;
};
interface CookieStore extends EventTarget {
Promise<CookieList> get(optional DOMString name);
Promise<CookieList> getAll();
Promise<CookieList> match((DOMString or Regexp) name, (DOMString or Regexp) path);
Promise<CookieList> matchAll((DOMString or Regexp) name, (DOMString or Regexp) path);
Promise<void> put(DOMString name, DOMString value, optional CookieOptions);
};
document.cookieStore.addEventListener("change", function(event) {
// event.detail == CookieList
});
partial interface Document {
CookieStore cookieStore;
};
partial interface ServiceWorkerGlobalScope {
CookieStore cookieStore;
};
partial interface InstallEvent {
void registerCookieInterest(...);
};
With the auto-deploy of the spec, the master branch and gh-pages branch should be cleaned up a bit.
In particular, the master branch doesn't need index.html anymore. For the gh-pages branch, we probably only need index.html and images. Definitely need .travis.yml and maybe the deploy keys.
It would be good to clean these up.
Some developers explicitly fake document.cookie behavior, for example in iframe sandboxes, such that their code can remain blissfully unaware that they do not have direct access to cookies. For example, they may set getters and setters for document.cookie to then postMessage to another context that has actual cookie access. This is notably even possible for cookie-averse documents.
It seems that, which this API, this type of faking should still be possible, but I'd love to see it as an explicit goal.
cookieStore.delete(options)
accepts CookieStoreSetOptions
. It occurs to me that some folks might like to use the CookieListItem
result of cookieStore.getAll()
and pass it directly to delete
, e.g.:
cookieStore.getAll().then(cookies => Promise.all(map(cookie => cookieStore.delete(cookie))));
Perhaps deleteAll
would satisfy that use case, but maybe I'd want to write an arbitrary filter selecting cookies to delete:
cookieStore.getAll().then(
cookies => Promise.all(
cookies.filter(cookie => cookie.name.length > 6)
.map(cookie => cookieStore.delete(cookie))
)
);
It seems like code like this could work. CookieListItem
(the result of getAll
) has a subset of the properties of CookieStoreSetOptions
. Will it work? Should it work?
This is a follow-up of #31 (comment). I am just opening a new issue so that we don't keep the discussion in an unrelated one.
This is a response for @bsittler observations.
I'm glad to see this isn't the only API that tries to change the default path for Set-Cookie ๐
This has come after much push back from js-cookie team. We just conceded because it was proven with many reports over the years that the default path rules violate the Principle of Least Astonishment for developers that work with cookies.
jQuery cookie didn't changed the Path
until the version 2.0. See this issue for some reported links and context on why that decision was made.
Answering to @bsittler questions:
Also, why synchronous? Is using await ... and/or .then(...) problematic for some reason?
Not at all, it is just that I thought it might make sense to have a synchronous API for compatibility purposes. Many implementations have a similar interface and they are pretty well established in the ecosystem:
This issue sums up very well why we should refrain from creating different APIs. In the last versions, js-cookie tried to be as similar as possible to the interface of other similar implementations to be more portable.
If there's a way to fix the problems that drove the creation of this proposal with a new cookie API that fixes a multitude of other problems (like the encoding issues), then I guess we could leverage this to do that too. Although I still need to understand in details what is the problem that drove the creation of this proposal.
I don't think that being asynchronous by default even when asynchronicity is not necessary would cause any harm other than unnecessary .then
s everywhere, but that's just theoretical purity.
Are there other event handlers that need to be blocked/prevented from firing until the cookie operation completes?
Even handlers from which kind? AFAIK there's no event handler in the synchronous document.cookie
API, it is just I/O. I probably didn't understand the question.
Or is this primarily due to a desire to use it in the inner implementation of pre-existing APIs already exporting script-blocking APIs for cookie operations?
If we could build something that mimics the behavior of an API like js-cookie we could gradually make it cease to exist. There's enough evidence that the features it supports are useful in the wild.
Besides that, there's no need to implement asynchronicity for a synchronous API if there is another standard equivalent that fixes the same issues. The problem is that if the standard don't address things like encoding issues or allows encoding to be overridden for server-side compatibility (like js-cookie converters), then we would need to keep abstracting document.cookie
and the new low-level asynchronous API in a library that complements with the features lacking in the standard.
It would be awesome if we could dedicate the effort to fix the document.cookie
API. Would it make sense?
Most modern browsers assume UTF-8 when exposing cookie data to scripts and <meta http-equiv=set-cookie ... >
, but IE and Edge use the system locale's "ANSI" codepage for this instead (using silent lossy conversion on write), causing a lack of interoperability in practice. The cookie jar itself seems to be byte-oriented and eight-bit-clean in all modern browsers. In practice, using URL-encoding or Base64 armoring is possible but adds a lot of overhead (encodeURIComponent
and escape
inflate characters up to 3x, base64 1.5x), decrease readability and debuggability (often the data is user-entered and users can use browser cookie jar inspectors to look at it), and (in the case of base64) don't have a built-in codec in IE. Length inflation also runs up against cookie length and cookie jar per-domain size caps.
As a result, sites storing non-ASCII data (often user input) in cookies either need to deal with some degree of cross-browser incompatibility or need to use an ugly and inefficient workaround. On the server-side, guessing based on User-Agent sniffing combined with approximation based on IP geolocation, Accept-Language analysis, and/or script-provided IE-specific navigator.systemLanguage
is the best hope for portably encoding/decoding cookies which will be shared with scripts and/or set in HTML.
Given all this, I think it would be nice to have the new async cookies API allow easy use of raw UTF-8 in all browsers but also provide a way to read and write cookies in the browser's default "cookie charset" as well as raw bytes.
https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/cookies
https://www.chromium.org/developers/design-documents/extensions/proposed-changes/apis-under-development/proposal-chrome-extensions-cookies-api
It's not clear whether the spec authors reviewed these APIs. It would be productive to get feedback from extension developers who have used these APIs, to see if they have any feedback. (It's very close to the proposed spec.)
This can either reference RFC 6265 Section 5.3, when the user agent "receives a cookie"... steps with an expiry time of 0 or it could delegate to the "set a cookie" algorithm. The latter is probably better.
An easy way to fix this is to move the return new Promise(...
to the top and then just throw errors as before. They will be automatically caught and converted into promise rejections.
has
/get
/getAll
take a path but don't do anything with it at the moment.
A ServiceWorker should be able to use a version of the polyfill where the async cookie jar access is implemented in a cooperating document accessed via postMessage
document.cookie reveals all cookies (sameSite or otherwise) but doesn't tell the script which ones have SameSite=lax or SameSite=strict. However, a ServiceWorker won't be able to know whether a cookie should be considered when handling a specific request unless it knows both:
It's how i'd expect most users to consume it
Just an idea. :)
It would be helpful and convenient to provide a way to delete cookies in bulk, especially if it allowed matchType: 'startsWith'
. Otherwise, developers would have to getAll
the cookies and delete them individually.
cookieStore.getAll().then(cookies => Promise.all(cookies.map(cookie => cookieStore.delete(cookie))));
The spec says that await cookieStore.delete('__Host-COOKIENAME')
is equivalent to a longer sample that expires the cookie:
let theVeryRecentPast = Date.now();
let expiredCookieSentinelValue = 'EXPIRED';
await cookieStore.set('__Secure-COOKIENAME', expiredCookieSentinelValue, {
path: '/cgi-bin/',
expires: theVeryRecentPast,
secure: true,
domain: 'example.org'
});
That doesn't look right to me. If anything it would be equivalent to this code which leaves the path and domain implicit:
let theVeryRecentPast = Date.now();
let expiredCookieSentinelValue = 'EXPIRED';
await cookieStore.set('__Secure-COOKIENAME', expiredCookieSentinelValue, {
expires: theVeryRecentPast,
});
Right now this API is very flexible. Issues like #3 and #1 play into a larger theme, which is what use cases this API is for.
Personally I think it might make sense to be more restrictive and only add features to this API that serve concrete use cases, instead of saying "anything you can do with document.cookie, you can do with this API." We definitely want to minimize the number of people that drop back to sync document.cookie, or people who can't accomplish their goals inside workers because their favorite cookie-related feature is missing. But I really am not sure that modern websites are purposefully using things like no-value cookies or extended attributes or cookie names/values that don't match the RFC's grammar.
So personally I'd prefer to start as restrictive as possible in those regards, and wait for users to surface use cases for more lenient behavior.
I realise that can't be polyfilled using document.cookie
, but are we going to have the same restriction?
W3C TAG's design principles says that enumeration values should be lowecase, dash-delimited. The matchType
dictionary member in cookieStore.get
/ cookieStore.getAll
options takes the values equals
and startsWith
. According to the TAG principles, the latter should be starts-with
.
cookieStore.getAll().then(
cookies => Promise.all(
cookies.filter(cookie => cookie.name.length > 6)
.map(cookie => cookieStore.delete(cookie))
)
);
Deleting a list of cookies is a mouthful, requiring Promise.all
and a map
. It would be nice if delete would accept an array.
cookieStore.getAll().then(
cookies => cookieStore.delete(
cookies.filter(cookie => cookie.name.length > 6)
)
);
In Opinions, it mentions that Secure
cookies would be treated specially by the API by not letting insecure origins touch them. I think this should be omitted. Chrome is currently releasing Strict Secure Cookies (https://www.chromestatus.com/feature/4506322921848832) which applies all of these properties to cookies across the board, including from document.cookie
. Firefox has already started implementing this as well, and we're in touch with other browser vendors to help make it happen. Similarly, I don't think cookie prefixes should be explicitly handled. IMO, these should continue to be handled at the cookie layer in networking, and not at the Web API layer
That having been said, I like that Secure
is the default from secure origins.
This discussion should probably move to WICG. I've started a topic there for it:
https://discourse.wicg.io/t/rfc-proposal-for-an-asynchronous-cookies-api/1652
Right now the polyfill fails silently rather than rejecting when the domain attribute is not valid.
@mikewest is working on https://tools.ietf.org/html/draft-west-origin-cookies-01 which looks pretty awesome, but it also appears to create a new, separate cookie jar for origin cookies. Should there be a separate navigator.originCookies to prevent confusion? Will documents also have a document.originCookie to allow reading this jar?
It would be really great to have Travis CI convert the bikeshed source to HTML whenever the master branch is updated.
This means that all spec edits should be on the master branch and the gh-pages branch should basically never be manually edited any more.
This should reference RFC 6265 Section 5.3, when the user agent "receives a cookie"... steps.
Related to #37
Should this API (working in the world of JavaScript) be allowed to create httpOnly
cookies?
This is currently not possible from JavaScript within the web page:
document.cookie = 'session1=abc-123; httpOnly';
document.cookie = 'session2=abc-123';
document.cookie
"session2=abc-123"
It's just a mild concern about session fixation; as it could be used by someone malicious to create a httpOnly
session cookie on the victims computer, where the attacker will then know what the value will be (as they set it)... that's not to say that they couldn't set the cookie without the httpOnly
flag.
The dictionaries CookeStoreGetOptions
and CookeStoreSetOptions
don't have any required members. This implies that the options parameters for the methods that have these parameters must be marked optional.
(Bikeshed produces a fatal error for this.)
The has
method doesn't strike me as useful enough to warrant the extra weight in the spec, docs, and browser code. get
resolves to null when has
would return false, and I expect that most code that would use has
can take advantage of the fact that null is falsey.
So, I'd like to remove the method from the WPT tests and from Chrome's implementation.
@domenic @inexorabletash Any thoughts?
Most "multimap" style APIs do not allow .get() with no args. (That is, they treat it as .get("undefined")
.) This seems to get the first cookie with any key? Is that useful? Seems like a weird part of API design.
In the "informative" sections that explain set and delete in terms of document.cookie, it might help to explain them in terms of headers as well, since that's how the RFC is written.
That might make the rationale of describing deletion in terms of expiry a bit clearer.
Error is reserved for user-defined APIs, not web APIs.
Choosing between TypeError and "SyntaxError" DOMException is always a bit tricky. I think I'd lean toward "SyntaxError" in this case but @annevk might disagree.
https://wicg.github.io/cookie-store/ doesn't have anything.
CookieStoreSetOptions includes expires
but not max-age
, which is currently supported by document.cookie
.
max-age
is very useful (more useful than expires
, in my experience) and should be included in the spec as an alternative to expires
.
This may depend on #11
When I get cookies via getAll
, it feels like I should just be able to pass them to delete
rather than having to split the object up into multiple arguments.
Currently .set(
allows passing arbitrary options that set extended attributes. Is this a necessary capability? I assume you did this because it's possible with document.cookie
?
I might move this to a second options bag instead of mixing it up inside the first one, if it is necessary.
If we don't allow writes we side-step a bunch of the issues around not being able to use the results from the read API in the write API due to cookie name per se being an insufficient key
Observers should always receive a consistent snapshot of the cookie store as part of each observed change so that they do not need to maintain their own backing and also so they do not need to worry about potential skew as a result of prematurely terminated event handlers.
The spec and browsers allow multiple cookies with the same name to be set for different path/domain values. Occasionally, this happens by mistake, typically by forgetting to specify a path for a cookie, creating a painful experience for developers.
Worse, when this happens, it's impossible to delete (expire) these cookies on the client side, unless you can guess the path of the cookie to delete; there's no way to ask the browser to tell you the paths of duplicate cookies.
Here's an example of the (rather common) bug. If this code runs in a subdirectory path, it will set two cookies, each of which must be independently deleted by specifying the correct path.
document.cookie = "cookie=root;path=/";
document.cookie = "cookie=unspecified";
console.log(document.cookie); // cookie=unspecified; cookie=root
document.cookie = "cookie=deleted;max-age=0";
console.log(document.cookie); // cookie=root
Currently, the CookieListItem definition includes the cookie's name and value, but it would be very helpful to include the path and domain, as well. This way, when multiple cookies with the same name are returned, you can inspect the cookie and tell the difference.
In #1 (comment) @bsittler mentioned just a simple API for async setting/getting of the cookie string, like document.cookie
except it can be used in workers and is async.
We chatted about this a bit and then he had a brilliant idea: "why not both?"
That is: we expand this current proposal with promise-returning getAsString()
and setAsString(s)
objects, which do exactly what document.cookie
does, just async and works-in-workers. UAs could implement this small subset almost immediately! In the meantime, we could work on making a nicer cookie API, like this repo already has, as additional methods (get
/getAll
/set
/etc.).
This also neatly resolves #7. We can make the nice API a bit more restrictive, with the escape hatch of {get,set}AsString to take care of the crazy esoteric cases like no-value cookies for anyone who really needs them.
The 'SameSite' cookie attribute (draft: https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-00 ) has received some public exposure, e.g. http://www.sjoerdlangkemper.nl/2016/04/14/preventing-csrf-with-samesite-cookie-attribute/
The attribute is live in Chrome: https://www.chromestatus.com/feature/4672634709082112
It is being tracked for possible inclusion in Firefox too: https://bugzilla.mozilla.org/show_bug.cgi?id=795346
The possible values for this attribute in WebIDL should probably be a nullable three-valued enumeration:
The document states, "... it may be desirable to restrict [the API's] use ... to secure origins running in secure contexts."
Personally, I think it should not be restricted. I don't see this as a "powerful feature" per se, as I don't really foresee a world where insecure origins don't have cookie access at all. And anything that allows more sane use of cookies, even from insecure origins, sounds like a win to me.
delete
is a keyword in JavaScript and C++. Even though it can be safely used in ES5+, it's still less ergonomic, as it doesn't work well with simple syntax highlighters and older tools. Let's follow the suit of APIs that use remove
, please.
Right now the polyfill fails loudly if you attempt to read or observe cookies in a different directory from the page. Instead it could perhaps open IFRAMEs to read cookies for other paths.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.