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
~secrettoDream.run. - Whether the request likely came through an HTTPS connection, to set the
Secureattribute. - 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-formbuilds secure forms on top of sessions, and introduces automatic handling of CSRF tokens.e-jsonsends and receives JSON instead!