2021-04-06 20:22:41 +03:00
..
2021-04-06 20:22:41 +03:00
2021-03-27 00:33:29 +03:00
2021-03-27 00:33:29 +03:00
2021-04-06 20:22:41 +03:00

c-cookie


Let's set our own cookie:

let () =
  Dream.run ~secret:"foo"
  @@ Dream.logger
  @@ fun request ->

    match Dream.cookie "ui.language" request with
    | Some value ->
      Printf.ksprintf
        Dream.respond "Your preferred language is %s!" (Dream.html_escape value)

    | None ->
      Dream.response "Set language preference; come again!"
      |> Dream.set_cookie "ui.language" "ut-OP" request
      |> Lwt.return
$ dune exec --root . ./cookie.exe

The first time you access this app, it sets up a language preference, ut-OP. This string is sent to the client in a ui.language cookie. On the next request, the client sends it back. The app retrieves and displays it.


The Dream.set_cookie function is a little odd — even though it transforms a response (by adding a Set-Cookie: header), it also takes a request as an argument. That's because it access certain fields of the request to set some fairly aggressive security defaults:

  • Cookie encryption, for which it accesses the encryption key. This is why we passed ~secret to Dream.run.
  • Whether the request likely came through an HTTPS connection, to set the Secure attribute.
  • The site prefix, to set the path (almost always /).
  • A combination of all of the above to try to set either __Host- or __Secure- prefixes.

Dream.set_cookie also sets other security defaults, but doesn't need to check the request for them.

All of these automatic choices can be overridden with the optional parameters of Dream.set_cookie.


You can ignore all of this for basic usage — the cookie getter, Dream.cookie “knows” how to parse such cookies, including automatic stripping of prefixes and decryption of the value.

So, if you use Dream.set_cookie and Dream.cookie together, you get the most secure settings automatically “for free.”


You may wish to not encrypt the cookie value, because...

  • it does not contain sensitive data,
  • it needs to be readable by the client, or
  • it is already encrypted.

In that case, pass ~encrypt:false to Dream.set_cookie and ~decrypt:false to Dream.cookie. However, then you need to escape the cookie value so that it does not contain =, ; or newlines, and undo the escaping after reading the value.

The easiest way to do that for general data is to use Dream.to_base64url and Dream.from_base64url.


Next steps:

  • d-form builds secure forms on top of sessions, and introduces automatic handling of CSRF tokens.
  • e-json sends and receives JSON instead!

Up to the tutorial index