rgrinberg / opium

Sinatra like web toolkit for OCaml

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

SameSite cookie attribute not being set

Favo02 opened this issue · comments

Hello, thanks for the beautiful library!

I am developing a REST API, sending some responses with Set-Cookie header, using Opium.Response.add_cookie_or_replace function.
I'd like to set the SameSite cookie attribute, but the ~same_site: optional parameter doesn't do the job. I tried setting it to `Strict, `Lax and `None but the cookie always shows None.

This is the piece of code I am using:

let set_cookie
  ?(cookie_key = "_session")
  ?(secret = Sihl.Configuration.read_secret ())
  ?(max_age = 3600L)
  ?(scope = "/")
  ?(same_site = "strict")
  ?(http_only = true)
  session
  resp
  =
  let signed_with = Opium.Cookie.Signer.make secret in
  let session = session |> List.to_seq |> StrMap.of_seq in
  let cookie_value = to_json session in
  let cookie = cookie_key, cookie_value in
  let same_site = match same_site with | "strict" -> `Strict | "lax" -> `Lax | _ -> `None in
  Opium.Response.add_cookie_or_replace
    ~sign_with:signed_with
    ~expires:(`Max_age max_age)
    ~scope:(Uri.of_string scope)
    ~same_site:same_site
    ~secure:true
    ~http_only:http_only
    cookie
    resp

I also tried a much simpler, unsuccessful:

let set_cookie session resp =
  Opium.Response.add_cookie_or_replace
    ~same_site:`Strict
    session
    resp

The problem seems to be in Cookie.to_set_cookie_header function, which simply ignores same site attribute.
I prepared a PR #291 to fix the issue, simply adding it.

let to_set_cookie_header t =
  let v = Printf.sprintf "%s=%s" (fst t.value) (snd t.value) in
  let v =
    match Uri.path t.scope with
    | "" -> v
    | path -> Printf.sprintf "%s; Path=%s" v path
  in
  let v =
    match Uri.host t.scope with
    | None -> v
    | Some domain -> Printf.sprintf "%s; Domain=%s" v domain
  in
+ let v =
+   match t.same_site with
+   | `None -> Printf.sprintf "%s; SameSite=None" v
+   | `Strict -> Printf.sprintf "%s; SameSite=Strict" v
+   | `Lax -> Printf.sprintf "%s; SameSite=Lax" v
+ in
  let v =
    match t.expires with
    | `Date ptime ->
      Printf.sprintf "%s; Expires=%s" v (Ptime.to_date_time ptime |> Date.serialize)
    | `Max_age max -> Printf.sprintf "%s; Max-Age=%s" v (Int64.to_string max)
    | `Session -> v
  in
  let v = if t.secure then Printf.sprintf "%s; Secure" v else v in
  let v = if t.http_only then Printf.sprintf "%s; HttpOnly" v else v in
  "Set-Cookie", v
;;