rgrinberg / opium

Sinatra like web toolkit for OCaml
MIT License
753 stars 68 forks source link

Session support #60

Open krtx opened 8 years ago

krtx commented 8 years ago

Is it planned to support session?

rgrinberg commented 3 years ago

Indeed it is planned. We should have something that's based on https://github.com/inhabitedtype/ocaml-session

tmattio commented 3 years ago

I've been experimenting with ocaml-session a bit to see how we can integrate it with Opium. Have to say I find the API a bit confusing. If we integrate it, it would be good to wrap it and try to identify a user-friendly API.

One thing that makes me think we will probably have to build our own solution is that ocaml-session does not support storing sessions in signed cookies (and is incompatible with it as of now), which is the most important use case IMHO.

rgrinberg commented 3 years ago

Is it not possible to create a a cookie backend for session?

tmattio commented 3 years ago

Not at the moment, the types for get and set in the backend interface are:

val get : t -> key -> (value * period, error) result
val set : ?expiry:period -> t -> key -> value -> unit

get takes only the backend instance as an argument, so either we create a new backend for each request, or we make the current request stateful. Neither of wich seems like a good solution.

Similarly, set should take the current response and return a response instead of ().

So, from what I can see, the API of ocaml-session would have to change its API quite a bit to support signed-cookie.

lindig commented 3 years ago

I would like to see cookie-based session support, too. I tried to implement something simple myself but without luck so far: set a cookie when a form posts correct credentials and later check for the presence of the cookie. I also struggle with composing the app such that some routes require authentication while others don't. It seems that once you have an authentication test in the stack, it affects all routes and not just the one below it.

aantron commented 3 years ago

@lindig I have selective session checking (and selective CSRF token checking) support built as middlewares based on this: https://github.com/rgrinberg/opium/issues/263#issuecomment-787061404

You can probably do something similar; else I can probably either PR some of these middlewares to Opium, or publish them in their own repo. I have a nice composable chain of these things, something like a nice logger with colors and request ids, followed by session checking (I do check them unconditionally, because I generate pre-sessions for non-logged-in users; the session middleware exposes a function for replacing the session if one of the handlers logs it in). This is followed by routing, with POST routes being strictly filtered by Content-type, which is then followed by CSRF token checking. Forms are loaded into pattern-matching-friendly values.

I even have middlewares for choosing the UI language, any path prefix, and the whole URL structure.

Overall, Opium middlewares seem to compose very well.

lindig commented 3 years ago

Thanks. I have solved the problem of requiring authentication only for some routes but my session support is primitive as it relies on simple tokens. My main function looks like this:

let main () =
  Logs.set_reporter (Logs_fmt.reporter ());
  Logs.set_level (Some Logs.Info);
  O.App.empty
  |> O.App.middleware O.Middleware.logger
  |> O.App.post "/login" @@ login ~succ:reset ~fail:reset
  |> O.App.post "/upload" @@ auth ~succ:upload ~fail:reset
  |> O.App.get "/logout" @@ logout reset
  |> O.App.get "/" @@ auth ~succ:index1 ~fail:index0
  |> O.App.cmd_name "Quad" |> O.App.run_command