Giter Club home page Giter Club logo

cookie-store's Introduction

Cookie Store API

CI

This repository documents an API for accessing HTTP cookies asynchronously from Document and Service Worker global contexts.

The API has a test suite in the Web Platform Tests project.

Resources

This API is inspired by and loosely based on the following discussions.

cookie change events is a concurrently developed API proposal that also addresses the synchronous nature of document.cookie.

The Cookie Store API has also been discussed in the following places.

The best resource for understanding the deep technical aspects of cookies is the most recent draft of RFC 6265bis.

This API aims to replace document.cookie and navigator.cookieEnabled.

This API is also known as the Async Cookies API.

cookie-store's People

Contributors

autokagami avatar ayuishii avatar bsittler avatar bunneyster avatar dcthetall avatar dfabulich avatar foolip avatar fred-wang avatar inexorabletash avatar marcoscaceres avatar oyiptong avatar patrickhulce avatar pwnall avatar recvfrom avatar remy avatar rtoy avatar saschanaz avatar tabatkins avatar tobie avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cookie-store's Issues

Get pr preview working

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.

Bytes vs. characters and "cookie charset"

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.

Deleting a cookie is awkward

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.

Specify the query cookies algorithm.

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?

cookieStore.remove instead of cookieStore.delete

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.

API should not specifically handle Secure cookies

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.

Larger philosophical point: should this be able to do everything document.cookie can do, or just a sane subset?

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.

Possible API Sketch

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(...);
};

Clean up master and gh-pages branch

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.

Methods should have optional CookieStore{Get,Set}Options?

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.)

Explainer: cookies != storage

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

Is support for extended attributes necessary?

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.

Offer a deleteAll method

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))));

Should include sameSite cookie attribute in write interface

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:

  • sameSite: 'no_restriction' or sameSite: null - the default behavior, equivalent to no SameSite attribute in Set-Cookie
  • sameSite: 'strict' - equivalent to SameSite=Strict in Set-Cookie, the cookie will only be sent in same-site contexts
  • sameSite: 'lax' - equivalent to SameSite=Lax in Set-Cookie, the cookie will be sent in same-site contexts and in top-level navigations

"startsWith" should be "starts-with"

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.

Should explicitly maintain way to fake cookie behavior

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.

Should include sameSite attribute in reading/monitoring APIs, at least inside ServiceWorker

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:

  • what SameSite category the cookie is in (if any; what this issue is about) and
  • what SameSite category the intercepted fetch or foreign is in (needs a separate issue in ServiceWorker)
    #36 covers the separate issue of exposing sameSite in the write interface, but both should probably use the same enum to represent the tristate

A few observations on existing cookie APIs

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 .thens 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?

String-based API *plus* nice cookie API

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.

Remove cookieStore.has

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?

CookieStoreSetOptions missing max-age

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.

Should a ServiceWorker be able to create HttpOnly cookies?

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.

Auto-deploy the HTML spec

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.

Is CookieListItem a valid parameter to delete?

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?

delete: "equivalent" code isn't equivalent

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,
});

Allow delete to accept an array

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)
  )
);

Should the API be usable from insecure origins?

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.

Support no-value cookies?

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

Include path and domain in get response

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.

Explain set/delete in terms of HTTP headers

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.

Should a ServiceWorker be able to read HttpOnly cookies?

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:

  • a keyed hmac/salted hash or other cryptographic fingerprint of the cookie value
  • an opaque value for the cookie usable in comparison operations and in IndexedDB but not convertible to string
  • a boolean indicating whether a given cookie is present

Also, returned cookie objects could indicate their httpOnly status alongside name and value

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.