Sessions
Managing sessions in naboris.
# Session Data
Many Naboris
types take the parameter 'sessionData
this represents a custom data type that will define session data that will be attached to an incoming request.
# Session Config
ServerConfig.setSessionConfig
will return a new server configuration with the desired
session configuration. This call consists of one required argument mapSession
and three optional arguments ~maxAge
, ~sidKey
, and ~secret
.
1let setSessionConfig: (~maxAge: int=?, ~sidKey: string=?, ~secret: string=?, option(string) => Lwt.t(option(Session.t('sessionData))), ServerConfig.t('sessionData)) => ServerConfig.t('sessionData);
1val setSessionConfig: ?maxAge: int -> ?sidKey: string -> ?secret: string -> string option -> 'sessionData Session.t option Lwt.t -> 'sessionData ServerConfig.t -> 'sessionData ServerConfig.t
sidKey
-string
(optional) - The key used to store the session id in browser cookies. Defaults to"nab.sid"
.maxAge
-int
(optional) - The max age of session cookies in seconds. Defaults to2592000
(30 days).secret
-string
(optional) - A secret string used to sign session id cookies.mapSession
- covered in the section below.
# Session Mapping
mapSession
is a special function that is used to set session data on an incoming request based on the requests cookies. The signature looks like: option(string) => Lwt.t(option(Naboris.Session.t('sessionData)))
. That's a complicated type signature that expresses that the request may or may not have a sessionId
; and given that fact it may or may not return a session.
1// ReasonML
2// Your custom data type
3type userData = {
4 userId: int,
5 username: string,
6 firstName: string,
7 lastName: string,
8 isAdmin: bool,
9};
10
11let serverConfig: Naboris.ServerConfig(userData) = Naboris.ServerConfig.create()
12 |> Naboris.ServerConfig.setSessionConfig(sessionId => switch(sessionId) {
13 | Some(id) =>
14 /* for the sake of this example we're not using ppx or infix */
15 /* lwt promises can be made much easier to read by using these */
16 Lwt.bind(getUserDataById(id),
17 userData => {
18 let session = Naboris.Session.create(id, userData);
19 Lwt.return(Some(session));
20 }
21 );
22 | None => Lwt.return(None);
23 })
24 |> Naboris.ServerConfig.setRequestHandler((route, req, res) => switch(Naboris.Route.meth(meth), Naboris.Route.path(route)) {
25 | (Naboris.Method.POST, ["login"]) =>
26 let (req2, res2, _sessionId) =
27 /* Begin a session */
28 Naboris.SessionManager.startSession(
29 req,
30 res,
31 {
32 userId: 1,
33 username: "foo",
34 firstName: "foo",
35 lastName: "bar",
36 isAdmin: false,
37 },
38 );
39 Naboris.Res.status(200, res2) |> Naboris.Res.text(req2, "OK");
40 | (Naboris.Method.GET, ["who-am-i"]) =>
41 /* Get session data from the request */
42 switch (Naboris.Req.getSessionData(req)) {
43 | None =>
44 Naboris.Res.status(404, res) |> Naboris.Res.text(req, "Not found")
45 | Some(userData) =>
46 Naboris.Res.status(200, res)
47 |> Naboris.Res.text(req, userData.username)
48 };
49 });
1(* OCaml *)
2(* Your custom session data *)
3type user_data = {
4 userId: int;
5 username: string;
6 first_name: string;
7 last_name: string;
8 is_admin: bool
9}
10
11let serverConfig: user_data Naboris.ServerConfiguserData = Naboris.ServerConfig.create ()
12 |> Naboris.ServerConfig.setSessionConfig (fun session_id ->
13 match (session_id) with
14 | Some(id) =>
15 (* for the sake of this example we're not using ppx or infix *)
16 (* lwt promises can be made much easier to read by using these *)
17 Lwt.bind (get_user_data_by_id id) (fun user_data ->
18 let session = Naboris.Session.create id user_data in
19 Lwt.return Some(session)
20 )
21 | None => Lwt.return None)
22 |> Naboris.ServerConfig.setRequestHandler (fun route, req, res ->
23 match ((Naboris.Route.meth route), (Naboris.Route.path route)) with
24 | (Naboris.Method.POST, ["login"]) ->
25 let (req2, res2, _session_id) =
26 (* Begin a session *)
27 Naboris.SessionManager.startSession req res {
28 userId= 1;
29 username= "foo";
30 first_name= "foo";
31 last_name= "bar";
32 is_admin= false
33 } in
34 Naboris.Res.status 200 res2
35 |> Naboris.Res.text req2, "OK"
36 | (Naboris.Method.GET, ["who-am-i"]) ->
37 (* Get session data from the request *)
38 match (Naboris.Req.getSessionData req) with
39 | None ->
40 Naboris.Res.status 404 res
41 |> Naboris.Res.text req "Not found"
42 | Some(user_data) ->
43 Naboris.Res.status 200 res
44 |> Naboris.Res.text req user_data.username)
# Starting Sessions
SessionManager.startSession
generates a new session id string
value and adds Set-Cookie
header to a new Res.t
. Useful for handling a login request.
1let startSession: (Req.t('sessionData), Res.t, 'sessionData) => (Req.t('sessionData), Res.t, string);
1val startSession: 'sessionData Req.t -> Res.t -> 'sessionData -> 'sessionData Req.t * Res.t * string
An example login request might look like this:
1| (Naboris.Method.POST, ["login"]) =>
2 let (req2, res2, _sid) =
3 Naboris.SessionManager.startSession(
4 req,
5 res,
6 TestSession.{username: "realsessionuser"},
7 );
8 Naboris.Res.status(200, res2) |> Naboris.Res.text(req2, "OK");
1| (Naboris.Method.POST, ["login"]) ->
2 let (req2, res2, _sid) = Naboris.SessionManager.startSession
3 req
4 res
5 TestSession.{username= "realsessionuser"} in
6 (Naboris.Res.status 200 res2) |> Naboris.Res.text req2 "OK"
# Invalidate Sessions
SessionManager.removeSession
adds Set-Cookie
header to a new Res.t
to expire the session id cookie. Useful for handling a logout request.
1let removeSession: (Req.t('sessionData), Res.t) => Res.t;
1val removeSession: 'sessionData Req.t -> Res.t -> Res.t
An example logout request might look like this:
1| (Naboris.Method.GET, ["logout"]) =>
2 Naboris.SessionManager.removeSession(req, res)
3 |> Naboris.Res.status(200)
4 |> Naboris.Res.text(req, "OK");
1| (Naboris.Method.GET, ["logout"]) ->
2 Naboris.SessionManager.removeSession req res
3 |> Naboris.Res.status 200
4 |> Naboris.Res.text req "OK";