Quantcast
Channel: NoRedInk
Viewing all articles
Browse latest Browse all 193

Maybe the Cookies API should not exist

$
0
0

One of the unique things about working at NoRedInk is that every other week we have “Elm Friday” where I pair with an engineer on something. Tessa and I worked on an alternate Array implementation. Noah and I made the navigation package for single-page apps. Scott helped create the react-elm-components package for embedding Elm in React apps. I think “Elm Friday” is wildly productive because each person brings unique perspective, interests, skills, knowledge, etc. and we augment each other. All of these are projects that are (1) great for the overall ecosystem and (2) projects that we would probably not have worked on individually.

Point is, Richard and I recently found ourselves working on elm-lang/cookie and came to a surprising conclusion: maybe it should not exist!

What are Cookies?

A cookie is a little piece of information associated with your domain. The important detail is that all that info gets collected and put into the Cookie header of every request you make. So if someone sets theme and user cookies, you would have a header like this on every HTTP request:

GET /spec.html HTTP/1.1 Host: www.example.org Cookie: theme=light; user=abc123 …

How do you create cookies though? One way is to use the Set-Cookie header when your server sends the page in the first place. For example, the server could set the theme and user cookies by providing a response like this one:

HTTP/1.0 200 OK Content-type: text/html Set-Cookie: theme=light Set-Cookie: user=abc123; Expires=Wed, 09 Jun 2021 10:18:14 GMT …

Okay, so that is the basic functionality. On top of that, JavaScript provides a truly awful cookie API that can be understood as exposing the Set-Cookie header functionality at runtime.

We are in the process of exposing all these Web Platform APIs so you can use them from Elm directly. Just doing a thin wrapper around JS APIs is easy and quick, but the whole point of this language is trying to do better. So I want to provide the functionality in a way that is delightful and works well with Elm. I think the websocket library is a great example of this! So in approaching cookies, we asked ourselves:

  • What exactly are the capabilities provided by the browser?
  • How do we expect people to use these capabilities?
  • Are there traps that people are likely to fall into?
  • How can we design our API such that “doing the right thing” is the default?

In the end, a great platform API fits into The Elm Architecture and the overall ecosystem in just the right way. Ideally users do not even realize that there were other options. They just are writing oddly solid apps and feeling pretty good about it.

As we explored these questions, it revealed that almost everything about cookies is a trap.

Cookies have Problems

Security

You may have heard of “tracking cookies” that let companies see which pages you have visited. Here is how that works:

  1. A company convinces as many websites as possible to embed an “Like button” by adding a simple <script> tag to their page. Share this recipe on Facebook, Twitter, and Pinterest!

  2. The <script> can load arbitrary code that runs with full permissions. The have access to everything from the document to whatever parts of your JS code are globally accessible.

The ultimate goal of this setup is to uniquely identify a user, which is where cookies come in.

Say you visit hooli.com. They set a cookie that uniquely identifies you. Later you go to a blog with a “Hooli Share” button which is embedded as a <script>. They can run some code that figures out what URL you are visiting, how long you are there, etc. When they have all the info they want, they send an HTTP request to hooli.com which automatically gets any hooli.com cookies. That way they get the data tagged with your unique identifier. That means they know exactly who you are as well as what sites you are visiting. Pretty neat trick!

Now, I had a vague notion that people track me online, but before looking into cookies, I had no idea it was so easy to get so much information. So this seems like a pretty bad problem to me, but I suspect enough money is made off this that it is likely to continue to exist.

Memory Caps and Encoding

When I first described cookies up above, I used a theme cookie. The idea there was that we have some app with a light and dark theme. Maybe people want a dark theme at night or something! But does it make sense to store that information in a cookie? Probably not.

First problem, browsers cap the memory available for cookies. Some person on the internet suggests that the limit is 4093 bytes for your whole domain. Pretty tiny! It sounds like in the olden times, when you got to the max size, you just had to wait until some cookies expired. Now it sounds like it will silently evict old cookies. Either way, pretty bad.

Second problem, cookies can contain a very limited set of characters. For example, Safari permits ASCII characters only. In the version we tested, it just ignores your attempt to set cookies if it sees any characters it does not like. So if you have a string with Unicode characters outside that range, it will break in various ways depending on the browser.

So for a vast majority of “storage” scenarios, this API is significantly weaker than localStorage and sessionStorage.

Note: Using a cookie would also mean that the data is attached to every single HTTP request you make. As we learned, that is not too much data, but why bother sending it if the whole point of browser storage is that you do not handle it in your server?

A Better Way?

As far as I can tell, the only usage that really makes sense is the following scenario:

A user comes to your website http://mail.example.com and you want to serve them their inbox immediately, no sign in necessary.

In that case, you do want to have some additional information added by the browser that can help reliably identify and authenticate the user. You want to know that and nothing else.

Based on a unique identifier, you can look up things like theme from your database and send back the appropriately styled HTML. Basically, any other information can be derived from a unique ID. In this world, people would have a consistent experience no matter what device they log in from. You also own the customization data, so if you do an upgrade such that theme works different now, you can change it all at once on your servers. You do not have to wait for the user to sign back in, keeping crazy fallback code forever.

So it sounds like the following constraints could help:

  • A domain can only store a unique identifier.
  • The only way to set this unique identifier is with an HTTP response header, like a restricted Set-Cookie.
  • That unique identifier is only added to HTTP requests headers if the page was served from the same domain the request is going to.

That means I can log in on hooli.com without Hooli permanently knowing who I am when I check out a French Toast recipe.

There are problems though. I use Google Analytics to learn how many people are checking out Elm, and it is actually pretty handy that they can distinguish between visits and unique visitors. One person visiting 10 times is very different than 10 people visiting one time each! I think I could still get that information, but my servers would have to have some extra logic enabled to assign unique IDs. So it would be a bit harder to set up, but for inocent questions like, “how can the Elm website be more helpful?” it seems like this scheme would still work.

It seems like there is a lot of money in keeping the current design, so who knows if something like this will ever happen!

What to do in Elm?

It was getting pretty late on Friday by the time Richard and I really loaded all this information into our brains. We had drafted a pretty nice API and were kind of tired based on how insane everything was.

As we were finalizing the API and writing documentation, I asked Richard, if you only want a unique identifier, and you only want to set it with a header like Set-Cookie, why are we even doing this? The one valid scenario did not require this API! Neither of us could think of a compelling reason to set cookies any other way. Especially considering that the browser’s localStorage API covers the only other plausible use with a higher data cap and proper unicode support.

Elm is very serious about having the right defaults. People should just do the easy and obvious thing, and somehow, as if by accident, end up with a codebase that is quite nice. So libraries need to be pleasant to use, of course, but they also need to rule out bad outcomes entirely. And as far as we can tell, this means the cookie library should not exist!

Note: Richard and I could not think of legitimate uses for this API, but that may be a lack of creativity or experience. Open a friendly issue here if you think you have a scenario that cannot be covered by the Set-Cookie header.


Evan Czaplicki
@czaplic
Engineer at NoRedInk


Viewing all articles
Browse latest Browse all 193

Trending Articles