diff --git a/CHANGES.md b/CHANGES.md
index fd96369..9229f62 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,3 +1,12 @@
+dev
+===
+
+ * Add proper titles to pages
+ * Store password with salt and SHA3_512 (GitHub PR #2)
+   - Bump schema version (to 4)
+ * Change session handling (GitHub PR #4)
+
+
 1.2.4 (2020-08-23)
 ==================
 
diff --git a/META.in b/META.in
index cf27702..3a9494b 100644
--- a/META.in
+++ b/META.in
@@ -1,4 +1,4 @@
-requires = "unix,extlib,postgresql,calendar,pcre,str"
+requires = "unix,extlib,postgresql,calendar,pcre,str,cryptokit,netstring"
 version = "%_NURPAWIKI_VERSION_%"
 archive(byte) = "nurpawiki.cma"
 archive(native) = "nurpawiki.cmxs"
diff --git a/_tags b/_tags
index 4f4fdf1..07b9b40 100644
--- a/_tags
+++ b/_tags
@@ -1,4 +1,4 @@
 "src": include
-<src/*>: thread, package(threads), package(netstring), package(calendar), package(extlib), package(postgresql), package(eliom.server), package(lwt_ppx)
+<src/*>: thread, package(threads), package(netstring), package(calendar), package(extlib), package(postgresql), package(eliom.server), package(lwt_ppx), package(cryptokit)
 <src/*.cmx>: for-pack(Nurpawiki)
 <src/nurpawiki.cmx>: -for-pack(Nurpawiki)
diff --git a/gen_ocsigen_config b/gen_ocsigen_config
index 6514a71..7cb273a 100755
--- a/gen_ocsigen_config
+++ b/gen_ocsigen_config
@@ -7,7 +7,7 @@
 
 set -e   # Bail out on errors
 
-site_cma="_build/nurpawiki.cma"
+site_cma="_build/src/nurpawiki.cma"
 static_root="."
 if [ -z $1 ]; then
     true
@@ -21,32 +21,6 @@ else
     fi
 fi
 
-stdlib_path=`ocamlfind ocamlc -where`
-
-str_path=`ocamlfind query str`
-nums_path="$stdlib_path"
-pcre_path=`ocamlfind query pcre`
-
-calendar_path=`ocamlfind query calendar`
-extlib_path=`ocamlfind query extlib`
-postgresql_path=`ocamlfind query postgresql`
-sqlite3_path=`ocamlfind query sqlite3`
-cryptokit_path=`ocamlfind query cryptokit`
-
-# The way we find Ocsigen METAS directory is extremely hacky.  I don't
-# know of a better way.
-ocsigen_metas_dir="`ocamlfind printconf stdlib`"
-
-if [ "$DEBUG" != "" ]; then
-    echo $str_path
-    echo $pcre_path
-    echo $calendar_path
-    echo $extlib_path
-    echo $postgresql_path
-    echo $sqlite3_path
-    echo $ocsigen_metas_dir
-fi
-
 mkdir -p var/log
 mkdir -p var/run
 
@@ -80,18 +54,9 @@ if [ "$DBPASSWD" = "" ]; then
 fi
 
 cat ocsigenserver.conf.in | \
-    sed -e "s|%_OCSIGEN_ROOT_%|$OCSIGEN_ROOT|g" | \
-    sed -e "s|%_STR_CMA_%|${str_path}/str.cma|g" | \
-    sed -e "s|%_NUMS_CMA_%|${nums_path}/nums.cma|g" | \
-    sed -e "s|%_CALENDAR_CMA_%|${calendar_path}/calendarLib.cmo|g" | \
-    sed -e "s|%_PCRE_CMA_%|${pcre_path}/pcre.cma|g" | \
-    sed -e "s|%_EXTLIB_CMA_%|${extlib_path}/extLib.cma|g" | \
-    sed -e "s|%_POSTGRESQL_CMA_%|${postgresql_path}/postgresql.cma|g" | \
-    sed -e "s|%_CRYPTOKIT_CMA_%|${cryptokit_path}/cryptokit.cma|g" | \
     sed -e "s|%_STATIC_ROOT_%|${static_root}|g" | \
     sed -e "s|%_NURPAWIKI_CMA_%|${site_cma}|g" | \
     sed -e "s|%_DBNAME_%|${DBNAME}|g" | \
     sed -e "s|%_DBPASSWD_%|${DBPASSWD}|g" | \
-    sed -e "s|%_SQLITE3_CMA_%|${sqlite3_path}/sqlite3.cma|g" | \
     sed -e "s|%_OCSIGEN_METAS_DIR_%|${ocsigen_metas_dir}|g" | \
     sed -e "s|%_DBUSER_%|${DBUSER}|g"
diff --git a/ocsigenserver.conf.in b/ocsigenserver.conf.in
index dd2d6f8..58345b9 100644
--- a/ocsigenserver.conf.in
+++ b/ocsigenserver.conf.in
@@ -6,19 +6,15 @@
 
     <charset>UTF-8</charset> <!-- Default charset for pages -->
 
-    <findlib path="%_OCSIGEN_METAS_DIR_%" />
-    <extension findlib-package="ocsigen.ext.staticmod" />
+    <extension findlib-package="ocsigenserver.ext.staticmod"/>
 
-    <library module="%_STR_CMA_%"/>
-    <library module="%_NUMS_CMA_%"/>
-    <library module="%_PCRE_CMA_%"/>
-    <library module="%_CALENDAR_CMA_%"/>
-    <library module="%_EXTLIB_CMA_%"/>
-    <library module="%_POSTGRESQL_CMA_%"/>
-    <library module="%_CRYPTOKIT_CMA_%"/>
+    <library findlib-package="num"/>
+    <library findlib-package="calendar"/>
+    <library findlib-package="extlib"/>
+    <library findlib-package="postgresql"/>
 
     <!-- Sqlite is needed by ocsipersist, which is used by Eliom: -->
-    <extension module="%_SQLITE3_CMA_%"/>
+    <extension findlib-package="sqlite3"/>
 
     <extension findlib-package="ocsigenserver.ext.ocsipersist-sqlite"/>
     <extension findlib-package="eliom.server"/>
@@ -42,5 +38,5 @@
     <commandpipe>./var/run/ocsigen_command</commandpipe>
 
   </server>
-  
+
 </ocsigen>
diff --git a/src/about.ml b/src/about.ml
index 1eb4885..5a28404 100644
--- a/src/about.ml
+++ b/src/about.ml
@@ -27,8 +27,11 @@ let about_page_html =
      [txt ("Nurpawiki v" ^ Version.version);
       br ();
       br ();
-      txt "Copyright © 2007-2008 Janne Hellsten";
-      txt "Copyright © 2008-2020 Stéphane Glondu";
+      txt "Copyright © 2006-2008 Janne Hellsten";
+      br ();
+      txt "Copyright © 2008-2022 Stéphane Glondu";
+      br ();
+      txt "Copyright © 2021 Fardale";
       br ();
       br ();
       txt "See the ";
@@ -37,10 +40,10 @@ let about_page_html =
       txt "."]]
 
 let _ =
-  Eliom_registration.Html.register about_page
+  Eliom_registration.Html.register ~service:about_page
     (fun () () ->
        Session.with_guest_login
          (fun cur_user ->
             return
-              (Html_util.html_stub
+              (Html_util.html_stub ~title:"About"
                  (Html_util.navbar_html ~cur_user about_page_html))))
diff --git a/src/database.ml b/src/database.ml
index e287801..f275350 100644
--- a/src/database.ml
+++ b/src/database.ml
@@ -61,13 +61,13 @@ module ConnectionPool =
                (match c#status with
                   Ok ->
                     f c
-                | Bad ->
+                | _ ->
                     Ocsigen_messages.errlog "Database connection bad.  Trying reset";
                     c#reset;
                     match c#status with
                       Ok ->
                         f c
-                    | Bad ->
+                    | _ ->
                         Ocsigen_messages.errlog "Database connection still bad.  Bail out";
                         raise (Error (Psql.Connection_failure "bad connection")))
            | None ->
@@ -582,15 +582,17 @@ let search_wikipage str = with_conn (fun conn ->
 let user_query_string =
   "SELECT id,login,passwd,real_name,email FROM nw.users"
 
-let user_of_sql_row row =
-  let id = int_of_string (List.nth row 0) in
-  {
-    user_id = id;
-    user_login = (List.nth row 1);
-    user_passwd = (List.nth row 2);
-    user_real_name = (List.nth row 3);
-    user_email = (List.nth row 4);
-  }
+let user_of_sql_row = function
+  | [user_id; user_login; user_passwd; user_real_name; user_email] ->
+    let user_id = int_of_string user_id in
+    {
+      user_id;
+      user_login;
+      user_passwd;
+      user_real_name;
+      user_email;
+    }
+  | _ -> invalid_arg "Database.user_of_sql_row: wrong number of elements in the row for an user"
 
 let query_users () = with_conn (fun conn ->
   let sql = user_query_string ^ " ORDER BY id" in
@@ -630,5 +632,5 @@ let update_user ~conn~user_id ~passwd ~real_name ~email =
 
 
 (* Highest upgrade schema below must match this version *)
-let nurpawiki_schema_version = 3
+let nurpawiki_schema_version = 4
 
diff --git a/src/database_upgrade.ml b/src/database_upgrade.ml
index 151ecaa..a244048 100644
--- a/src/database_upgrade.ml
+++ b/src/database_upgrade.ml
@@ -214,6 +214,19 @@ let upgrade_schema_from_2 ~conn logmsg =
   (* TODO seqs, findwikipage *)
   logged_exec ~conn logmsg "UPDATE nw.version SET schema_version = 3"
 
+(* Version 3 -> 4: change user password handling *)
+let upgrade_schema_from_3 ~conn logmsg =
+
+
+  (* increase this size of the passwd column to be able to store longer hash *)
+  logged_exec ~conn logmsg "ALTER TABLE nw.users ALTER COLUMN passwd TYPE character varying(256)";
+
+  (* add prefix for the new password storage schema *)
+  logged_exec ~conn logmsg "UPDATE nw.users SET passwd = CONCAT('$1$',passwd)";
+
+  (* Update the schema_version number to 4 *)
+  logged_exec ~conn logmsg "UPDATE nw.version SET schema_version = 4"
+
 (* TODO clean up *)
 let db_schema_version_raw conn =
   if table_exists ~conn ~schema:"nw" ~table:"version" then
@@ -246,6 +259,11 @@ let upgrade_schema_raw conn =
       Buffer.add_string logmsg "Schema is at version 2\n";
       upgrade_schema_from_2 ~conn logmsg
     end;
+  if db_schema_version_raw conn = 3 then
+    begin
+      Buffer.add_string logmsg "Schema is at version 3\n";
+      upgrade_schema_from_3 ~conn logmsg
+    end;
   assert (db_schema_version_raw conn == nurpawiki_schema_version);
   Buffer.contents logmsg
 
diff --git a/src/history.ml b/src/history.ml
index 551e6d0..8c51054 100644
--- a/src/history.ml
+++ b/src/history.ml
@@ -203,7 +203,7 @@ let view_history_page ~cur_user ~nth_page =
                      [tr [td ~a:[a_class ["no_break"; "h_date_heading"]] date_text]] @ lst_acc,
                    prettified_date))
                activity_groups ([],"")))) in
-  return & Html_util.html_stub
+  return & Html_util.html_stub ~title:"History"
     (Html_util.navbar_html ~cur_user
        ([h1 [txt "Blast from the past"]] @
           (page_links nth_page n_total_pages) @ [br (); br ()] @
@@ -211,7 +211,7 @@ let view_history_page ~cur_user ~nth_page =
 
 (* /history *)
 let _ =
-  Eliom_registration.Html.register history_page
+  Eliom_registration.Html.register ~service:history_page
     (fun nth_page () ->
        Session.with_guest_login
          (fun cur_user ->
diff --git a/src/html_util.ml b/src/html_util.ml
index 6b4a4d3..bc9a897 100644
--- a/src/html_util.ml
+++ b/src/html_util.ml
@@ -27,20 +27,20 @@ open Types
 open Services
 
 let make_static_uri name =
-  make_uri (static_dir ()) name
+  make_uri ~service:(static_dir ()) name
 
 let disconnect_box s =
   a ~service:disconnect_page [txt s] ()
 
 (* Use this as the basis for all pages.  Includes CSS etc. *)
-let html_stub ?(javascript=[]) body_html =
+let html_stub ?(javascript=[]) ~title body_html =
   let script src =
     js_script ~a:[a_defer ()] ~uri:(make_static_uri src) () in
   let scripts  =
     script ["nurpawiki.js"] :: (List.map script javascript) in
   html ~a:[a_xmlns `W3_org_1999_xhtml]
     (head
-       (title (txt ""))
+       (Eliom_content.Html.F.title (txt (title ^ " — Nurpawiki")))
        ((scripts) @
           [css_link ~a:[] ~uri:(make_uri ~service:(static_dir ())
                                   ["style.css"]) ();
@@ -68,7 +68,7 @@ let navbar_html ~cur_user ?(top_info_bar=[]) ?(wiki_revisions_link=[]) ?(wiki_pa
        txt "History"] None in
 
   let search_input =
-    [get_form search_page
+    [get_form ~service:search_page
        (fun (chain : ([`One of string] param_name)) ->
           [p [input ~input_type:`Submit ~value:"Search" Form.string;
               input ~input_type:`Text ~name:chain Form.string]])] in
@@ -136,7 +136,7 @@ let error text =
   span ~a:[a_class ["error"]] [txt text]
 
 let error_page msg =
-  html_stub
+  html_stub ~title:"Error"
     [p [error msg]]
 
 
diff --git a/src/main.ml b/src/main.ml
index c62cc99..3493752 100644
--- a/src/main.ml
+++ b/src/main.ml
@@ -254,7 +254,7 @@ module WikiML =
           let t = if text = "" then page else text in
           let%lwt b = Db.wiki_page_exists page in
           if b then
-            a wiki_view_page [txt t] (page, (None, (None, None))) |> return
+            a ~service:wiki_view_page [txt t] (page, (None, (None, None))) |> return
           else
             a ~a:[a_class ["missing_page"]]
               ~service:wiki_view_page [txt t]
@@ -262,7 +262,7 @@ module WikiML =
         else (* External link *)
           let url = scheme^":"^page in
           let t = if text = "" then url else text in
-          return & Raw.a ~a:[a_href (uri_of_string (fun () -> url))] [txt t] in
+          return & Raw.a ~a:[a_href (uri_of_string (fun () -> url))] [ext_img; txt t] in
 
       let add_html html_acc html =
         html::html_acc in
@@ -519,14 +519,14 @@ let view_page ~cur_user ?(revision_id=None) page_id page_name ~printable =
   if printable <> None && Option.get printable = true then
     let%lwt page_content =
       wikiml_to_html ~cur_user page_id page_name ~revision_id todos in
-    return & Html_util.html_stub page_content
+    return & Html_util.html_stub ~title:page_name page_content
   else
     let%lwt page_content =
       (wiki_page_contents_html
          ~cur_user
          page_id page_name ~revision_id todos ())
     in
-    return & Html_util.html_stub page_content
+    return & Html_util.html_stub ~title:page_name page_content
 
 (* Parse existing todo's from the current to-be-saved wiki page and
    update the DB relation on what todos are on the page.
@@ -696,7 +696,7 @@ let _ =
     let wikitext =
       String.concat "\n" (WikiML.wikitext_of_preprocessed_lines preproc_wikitext) in
     let f =
-      post_form service_save_page_post
+      post_form ~service:service_save_page_post
         (fun chain ->
            [(p [input ~input_type:`Submit ~value:"Save" Form.string;
                 Html_util.cancel_link wiki_view_page
@@ -709,10 +709,10 @@ let _ =
       wiki_page_contents_html ~cur_user
          ~revision_id:None
          page_id page_name page_todos ~content:[f] () in
-    return & Html_util.html_stub h
+    return & Html_util.html_stub ~title:("Edit " ^ page_name) h
   in
 
-  Eliom_registration.Html.register wiki_edit_page
+  Eliom_registration.Html.register ~service:wiki_edit_page
     (fun page_name () ->
        Session.with_user_login
          (fun cur_user ->
@@ -725,13 +725,13 @@ let view_wiki_page ~cur_user (page_name, (printable, (revision_id, _))) =
       view_page ~cur_user ~revision_id page_id page_name ~printable
   | None ->
       let f =
-        a wiki_edit_page [txt "Create new page"] page_name in
+        a ~service:wiki_edit_page [txt "Create new page"] page_name in
       let%lwt h = wiki_page_menu_html ~cur_user page_name [f] in
-      return & Html_util.html_stub h
+      return & Html_util.html_stub ~title:page_name h
 
 (* /view?p=Page *)
 let _ =
-  Eliom_registration.Html.register wiki_view_page
+  Eliom_registration.Html.register ~service:wiki_view_page
     (fun ((_, (_, (_, force_login))) as params) () ->
        (* If forced login is not requested, we'll let read-only guests
           in (if current configuration allows it) *)
@@ -763,7 +763,7 @@ let _ =
            (head (title (txt "")) [])
            (body [p [txt "invalid 'test' param!"]])) in
 
-  Eliom_registration.Html.register benchmark_page
+  Eliom_registration.Html.register ~service:benchmark_page
     (fun test_id () ->
        return (gen_html test_id))
 
@@ -801,15 +801,16 @@ let _ =
             | SR_todo -> assert false) search_results) in
   let gen_search_page ~cur_user search_str =
     let%lwt search_results = Db.search_wikipage search_str in
+    let title = "Search results" in
     return
-      (Html_util.html_stub
+      (Html_util.html_stub ~title
          (Html_util.navbar_html ~cur_user
-            ([h1 [txt "Search results"]] @ (render_results search_results))))
+            ([h1 [txt title]] @ (render_results search_results))))
   in
 
-  Eliom_registration.Html.register search_page
+  Eliom_registration.Html.register ~service:search_page
     (fun search_str () ->
        Session.with_guest_login
          (fun cur_user ->
-            gen_search_page cur_user search_str))
+            gen_search_page ~cur_user search_str))
 
diff --git a/src/page_revisions.ml b/src/page_revisions.ml
index da338c7..7cdfb79 100644
--- a/src/page_revisions.ml
+++ b/src/page_revisions.ml
@@ -53,13 +53,14 @@ let view_page_revisions page_descr =
   Session.with_guest_login
     (fun cur_user ->
        revision_table page_descr >>= fun revisions ->
+       let title = page_descr ^ " Revisions" in
        return
-         (Html_util.html_stub
+         (Html_util.html_stub ~title
             (Html_util.navbar_html ~cur_user
-               (h1 [txt (page_descr ^ " Revisions")] :: revisions))))
+               (h1 [txt title] :: revisions))))
 
 (* /page_revisions?page_id=<id> *)
 let _ =
-  Eliom_registration.Html.register page_revisions_page
+  Eliom_registration.Html.register ~service:page_revisions_page
     (fun page_descr () ->
        view_page_revisions page_descr)
diff --git a/src/password.ml b/src/password.ml
new file mode 100644
index 0000000..54df3d0
--- /dev/null
+++ b/src/password.ml
@@ -0,0 +1,41 @@
+open Cryptokit
+
+let random_salt size = Random.(string secure_rng size)
+
+(* Return Result.Ok (update, auth) where auth is true if the auth is successful
+   and update is true if the hash need to be updated *)
+let check stored_hash password =
+  let hash_decode = Hexa.decode () in
+  match String.split_on_char '$' stored_hash with
+  | [ ""; "1"; password_hash ] -> (
+      try
+        let hash_function = Hash.md5 () in
+        hash_decode#put_string password_hash;
+        hash_decode#finish;
+        hash_function#add_string password;
+        Result.Ok (true, string_equal hash_decode#get_string hash_function#result)
+      with Error _ -> Result.Error "Failure reading stored hash")
+  | [ ""; "8"; salt; password_hash ] -> (
+      try
+        let salt_decode = Hexa.decode () in
+        salt_decode#put_string salt;
+        salt_decode#finish;
+        let hash_function = Hash.sha3 512 in
+        hash_decode#put_string password_hash;
+        hash_decode#finish;
+        hash_function#add_string (password^salt_decode#get_string);
+        Result.Ok (false, string_equal hash_decode#get_string hash_function#result)
+      with Error _ -> Result.Error "Failure reading stored hash")
+  | _ -> Result.Error "Unknown storage format"
+
+let salt password =
+  let salt_encode = Hexa.encode () in
+  let salt = random_salt 8 in
+  salt_encode#put_string salt;
+  salt_encode#finish;
+  let hash_function = Hash.sha3 512 in
+  let hash_encode = Hexa.encode () in
+  hash_function#add_string (password ^ salt);
+  hash_encode#put_string hash_function#result;
+  hash_encode#finish;
+  Printf.sprintf "$8$%s$%s" salt_encode#get_string hash_encode#get_string
diff --git a/src/scheduler.ml b/src/scheduler.ml
index 8c28197..2667d67 100644
--- a/src/scheduler.ml
+++ b/src/scheduler.ml
@@ -139,10 +139,10 @@ let view_scheduler_page ~cur_user =
          ] in
 
     let table' =
-      Form.post_form edit_todo_page table (ET_scheduler, None) in
+      Form.post_form ~service:edit_todo_page table (ET_scheduler, None) in
 
     return &
-    Html_util.html_stub ~javascript:[["nurpawiki_scheduler.js"]]
+    Html_util.html_stub ~title:"Scheduler" ~javascript:[["nurpawiki_scheduler.js"]]
       (Html_util.navbar_html ~cur_user
          ([h1 [txt "Road ahead"]] @ [table'])) in
 
@@ -157,7 +157,7 @@ let render_edit_todo_cont_page ~cur_user = function
 
 (* /scheduler *)
 let _ =
-  Eliom_registration.Html.register scheduler_page
+  Eliom_registration.Html.register ~service:scheduler_page
     (fun todo_id () ->
        Session.with_guest_login
          (fun cur_user ->
@@ -167,7 +167,7 @@ let scheduler_page_discard_todo_id =
   Eliom_registration.Html.create
     ~path:(Path ["scheduler"])
     ~meth:(Get ((user_type
-                   et_cont_of_string string_of_et_cont "src_service")))
+                   ~of_string:et_cont_of_string ~to_string:string_of_et_cont "src_service")))
     (fun src_page_cont () ->
        Session.with_user_login
          (fun cur_user ->
@@ -271,7 +271,7 @@ let rec render_todo_editor ~cur_user (src_page_cont, todos_to_edit) =
 
 
   return &
-  Html_util.html_stub ~javascript:calendar_js
+  Html_util.html_stub ~title:"Edit TODO(s)" ~javascript:calendar_js
     (Html_util.navbar_html ~cur_user
        ((h1 heading)::[help_str; br(); f]))
 
@@ -289,7 +289,7 @@ let render_todo_get_page ~cur_user (src_page_cont, todo) =
       render_edit_todo_cont_page ~cur_user src_page_cont
 
 let _ =
-  Eliom_registration.Html.register edit_todo_get_page
+  Eliom_registration.Html.register ~service:edit_todo_get_page
     (fun get_params () ->
        Session.with_user_login
          (fun cur_user ->
@@ -312,7 +312,7 @@ let parse_todo_ids todo_ids =
 
 
 let _ =
-  Eliom_registration.Html.register edit_todo_page
+  Eliom_registration.Html.register ~service:edit_todo_page
     (fun (src_page_cont, single_tid) (todo_ids : (string * string) list) ->
        Session.with_user_login
          (fun cur_user ->
diff --git a/src/services.ml b/src/services.ml
index 00fbef2..3cfa300 100644
--- a/src/services.ml
+++ b/src/services.ml
@@ -38,7 +38,7 @@ let scheduler_page = create ~path:(Path ["scheduler"]) ~meth:(Get unit) ()
 
 let edit_todo_get_page = create ~path:(Path ["edit_todo"])
   ~meth:(Get ((user_type
-      et_cont_of_string string_of_et_cont "src_service") **
+      ~of_string:et_cont_of_string ~to_string:string_of_et_cont "src_service") **
      (opt (int "tid")))) ()
 
 let edit_todo_page =
diff --git a/src/session.ml b/src/session.ml
index dbb8270..7c234c1 100644
--- a/src/session.ml
+++ b/src/session.ml
@@ -28,25 +28,29 @@ open Config
 module Db = Database
 module Dbu = Database_upgrade
 
-let seconds_in_day = 60.0 *. 60.0 *. 24.0
-
-let scope_hierarchy = Eliom_common.create_scope_hierarchy "nurpawiki_session_data"
-let scope = `Session scope_hierarchy
-
-let login_eref = Eliom_reference.eref
-  ~scope
-  ~persistent:"login_info" None
-
-(* Set password & login into session.  We set the cookie expiration
-   into 24h from now so that the user can even close his browser
-   window, re-open it and still retain his logged in status. *)
-let set_password_in_session login_info =
-  let open Eliom_state in
-  let cookie_scope = scope in
-  set_service_state_timeout ~cookie_scope None;
-  set_persistent_data_state_timeout ~cookie_scope None >>= fun () ->
-  set_persistent_data_cookie_exp_date ~cookie_scope (Some 3153600000.0) >>= fun () ->
-  Eliom_reference.set login_eref (Some login_info)
+type login_status = Failed | User of Types.user
+
+let expiration_time = 60.0 *. 60.0 *. 24.0
+(* 24h *)
+
+let scope = Eliom_common.default_session_scope
+
+let group_scope = Eliom_common.default_group_scope
+
+let login_eref = Eliom_reference.eref ~scope:group_scope None
+
+(* Set user session in the group session, so login_eref is kept
+   synchronized between each session. *)
+let set_login_status_in_session login_status =
+  begin
+    match login_status with
+    | Failed -> Lwt.return_unit
+    | User user ->
+        Eliom_state.set_volatile_data_session_group ~scope user.user_login;
+        Eliom_state.set_service_session_group ~scope user.user_login;
+        Eliom_state.set_persistent_data_session_group ~scope user.user_login
+  end
+  >>= fun () -> Eliom_reference.set login_eref (Some login_status)
 
 let upgrade_page = create ~path:(Path ["upgrade"]) ~meth:(Get unit) ()
 
@@ -63,8 +67,8 @@ let link_to_nurpawiki_main sp =
     [txt "Take me to Nurpawiki"]
     (Config.site.cfg_homepage,(None,(None,None)))
 
-(* Get logged in user as an option *)
-let get_login_user () =
+(* Get logged in status as an option *)
+let get_login_status () =
   Eliom_reference.get login_eref
 
 let db_upgrade_warning () =
@@ -103,10 +107,10 @@ let login_html ~err =
      txt " if you're logging in for the first time.";
      br ()] in
 
-  Html_util.html_stub
+  Html_util.html_stub ~title:"Login"
     [div ~a:[a_id "login_outer"]
        [div ~a:[a_id "login_align_middle"]
-          [post_form connect_action
+          [post_form ~service:connect_action
              (fun (loginname,passwd) ->
                 [table ~a:[a_class ["login_box"]]
                    [tr [td ~a:[a_class ["login_text"]]
@@ -127,11 +131,11 @@ let with_db_installed f =
      an upgrade. *)
   let%lwt b = Dbu.is_schema_installed () in
   if not b then
-    return (Html_util.html_stub (db_installation_error ()))
+    return (Html_util.html_stub ~title:"Database installation error" (db_installation_error ()))
   else
     let%lwt v = Dbu.db_schema_version () in
     if v < Db.nurpawiki_schema_version then
-      return (Html_util.html_stub (db_upgrade_warning ()))
+      return (Html_util.html_stub ~title:"Database upgrade warning" (db_upgrade_warning ()))
     else f ()
 
 (** Wrap page service calls inside with_user_login to have them
@@ -139,26 +143,11 @@ let with_db_installed f =
     not logged in. *)
 let with_user_login ?(allow_read_only=false) f =
   let login () =
-    get_login_user ()
+    get_login_status ()
     >>= function
-      | Some (login,passwd) ->
-          begin
-            Db.query_user login
-            >>= function
-              | Some user ->
-                  let passwd_md5 = Digest.to_hex (Digest.string passwd) in
-                  (* Autheticate user against his password *)
-                  if passwd_md5 <> user.user_passwd then
-                    return
-                      (login_html
-                         [Html_util.error ("Wrong password given for user '"^login^"'")])
-                  else
-                    f user
-              | None ->
-                  return
-                    (login_html
-                       [Html_util.error ("Unknown user '"^login^"'")])
-          end
+      | Some (User user) -> f user
+      | Some Failed -> Eliom_reference.unset login_eref
+          >>= fun () -> return (login_html ~err:[Html_util.error ("Bad login or password")])
       | None ->
           if allow_read_only && Config.site.cfg_allow_ro_guests then
             let guest_user =
@@ -171,7 +160,7 @@ let with_user_login ?(allow_read_only=false) f =
               } in
             f guest_user
           else
-            return (login_html [])
+            return (login_html ~err:[])
   in
   with_db_installed login
 
@@ -190,30 +179,17 @@ let with_guest_login f =
 let action_with_user_login f =
   let%lwt db_version = Dbu.db_schema_version () in
   if db_version = Db.nurpawiki_schema_version then
-    get_login_user ()
+    get_login_status ()
     >>= function
-      | Some (login,passwd) ->
-          begin
-            Db.query_user login
-            >>= function
-              | Some user ->
-                  let passwd_md5 = Digest.to_hex (Digest.string passwd) in
-                  (* Autheticate user against his password *)
-                  if passwd_md5 = user.user_passwd then
-                    f user
-                  else
-                    return ()
-              | None ->
-                  return ()
-          end
-      | None -> return ()
+      | Some (User user) -> f user
+      | _ -> return ()
  else
    return ()
 
 
-let update_session_password login new_password =
+let update_session user =
   Eliom_state.discard ~scope () >>= fun () ->
-  set_password_in_session (login, new_password)
+  set_login_status_in_session user
 
 (* Check session to see what happened during page servicing.  If any
    actions were called, some of them might've set values into session
@@ -234,43 +210,61 @@ let any_task_priority_changes () =
   with Not_found ->
     None
 
-let connect_action_handler () login_nfo =
+let connect_action_handler () (login, passwd) =
   Eliom_state.discard ~scope () >>= fun () ->
-    set_password_in_session login_nfo >>= fun () ->
-      return ()
+  Db.query_user login >>= function
+  | Some user -> (
+      (* Authenticate user against his password *)
+      match Password.check user.user_passwd passwd with
+      | Result.Ok (update, auth) ->
+          if not auth then set_login_status_in_session Failed
+          else
+            (if update then
+             Db.with_conn (fun conn ->
+                 Db.update_user ~conn ~user_id:user.user_id
+                   ~passwd:(Some (Password.salt passwd))
+                   ~real_name:user.user_real_name ~email:user.user_email)
+            else return_unit)
+            >>= fun () -> set_login_status_in_session (User user)
+      | Result.Error e -> set_login_status_in_session Failed (*TODO: change this *))
+  | None ->
+      set_login_status_in_session Failed
 
 let () =
-  Eliom_registration.Action.register ~service:connect_action connect_action_handler
+  Eliom_registration.Action.register
+  ~service:connect_action connect_action_handler
 
 (* /schema_install initializes the database schema (if needed) *)
-let _ =
-  Eliom_registration.Html.register schema_install_page
+let () =
+  Eliom_registration.Html.register ~service:schema_install_page
     (fun () () ->
        Database_schema.install_schema ();%lwt
+       let title = "Database installation completed" in
        return
-         (Html_util.html_stub
-            [h1 [txt "Database installation completed"];
+         (Html_util.html_stub ~title
+            [h1 [txt title];
              p [br ();
                 link_to_nurpawiki_main ()]]))
 
 (* /upgrade upgrades the database schema (if needed) *)
-let _ =
-  Eliom_registration.Html.register upgrade_page
+let () =
+  Eliom_registration.Html.register ~service:upgrade_page
     (fun () () ->
        let%lwt msg = Dbu.upgrade_schema () in
+       let title = "Upgrade database schema" in
        return
-         (Html_util.html_stub
-            [h1 [txt "Upgrade DB schema"];
+         (Html_util.html_stub ~title
+            [h1 [txt title];
              (pre [txt msg]);
              p [br ();
                 link_to_nurpawiki_main ()]]))
 
-let _ =
-  Eliom_registration.Html.register disconnect_page
+let () =
+  Eliom_registration.Html.register ~service:disconnect_page
     (fun () () ->
        Eliom_state.discard ~scope () >>= fun () ->
         return
-          (Html_util.html_stub
+          (Html_util.html_stub ~title:"Logout"
              [h1 [txt "Logged out!"];
               p [br ();
                  link_to_nurpawiki_main ()]]))
diff --git a/src/user_editor.ml b/src/user_editor.ml
index 071e05b..e0a60fc 100644
--- a/src/user_editor.ml
+++ b/src/user_editor.ml
@@ -64,11 +64,11 @@ let rec view_user_admin_page ~err ~cur_user =
                td [a ~service:edit_user_page [txt "Edit"]
                      (Some "user_admin", user.user_login)]])
          users) in
-
+  let title = "Edit users" in
   return
-    (Html_util.html_stub
+    (Html_util.html_stub ~title
        (Html_util.navbar_html ~cur_user
-          ([h1 [txt "Edit users"];
+          ([h1 [txt title];
             users_table] @
              err @
             [post_form ~service:service_create_new_user
@@ -118,18 +118,18 @@ let save_user ~update_user ~login ~passwd ~passwd2 ~real_name ~email =
         return [Html_util.error "Re-typed password doesn't match your password!"]
       else
         begin
-          let passwd_md5 = Digest.to_hex (Digest.string passwd) in
+          let passwd_hash = Password.salt passwd in
           if update_user then
             begin
               match old_user with
                 Some u ->
                   (* If no password was entered, set it to old value: *)
-                  let new_passwd_md5 =
-                    if passwd = "" then None else Some passwd_md5 in
+                  let new_passwd_hash =
+                    if passwd = "" then None else Some passwd_hash in
                   Db.with_conn
                     (fun conn ->
                        Db.update_user ~conn
-                         ~user_id:u.user_id ~passwd:new_passwd_md5 ~real_name ~email)
+                         ~user_id:u.user_id ~passwd:new_passwd_hash ~real_name ~email)
                   >>= fun _ -> return []
               | None ->
                   assert false
@@ -137,12 +137,12 @@ let save_user ~update_user ~login ~passwd ~passwd2 ~real_name ~email =
           else
             Db.with_conn
               (fun conn ->
-                 Db.add_user ~conn ~login ~passwd:passwd_md5 ~real_name ~email)
-            >>= fun _ -> return []
+                 Db.add_user ~conn ~login ~passwd:passwd_hash ~real_name ~email)
+            >>= fun () -> return []
         end
 
 let _ =
-  Eliom_registration.Html.register service_create_new_user
+  Eliom_registration.Html.register ~service:service_create_new_user
     (fun () (login, (passwd, (passwd2, (real_name, email))))  ->
        Session.with_user_login
          (fun cur_user ->
@@ -179,7 +179,7 @@ let save_user_prefs c_passwd c_passwd2 (c_name,old_name) (c_email,old_email) =
         ])
 
 let _ =
-  Eliom_registration.Html.register user_admin_page
+  Eliom_registration.Html.register ~service:user_admin_page
     (fun _ () ->
        Session.with_user_login
          (fun cur_user ->
@@ -190,9 +190,10 @@ let _ =
 
 
 let rec view_edit_user_page caller ~err ~cur_user user_to_edit =
-  Html_util.html_stub
+  let title = "Edit User" in
+  Html_util.html_stub ~title
     (Html_util.navbar_html ~cur_user
-       ([h1 [txt "Edit User"]] @
+       ([h1 [txt title]] @
           err @
           [post_form ~service:service_save_user_edit
              (fun (passwd,(passwd2,(name,email))) ->
@@ -204,7 +205,7 @@ let rec view_edit_user_page caller ~err ~cur_user user_to_edit =
 
 
 let _ =
-  Eliom_registration.Html.register service_save_user_edit
+  Eliom_registration.Html.register ~service:service_save_user_edit
     (fun (caller, login) (passwd, (passwd2, (real_name, email)))  ->
        Session.with_user_login
          (fun cur_user ->
@@ -217,11 +218,14 @@ let _ =
                            ~update_user:true
                            ~login:login
                            ~passwd ~passwd2 ~real_name ~email >>= fun err ->
-                       (* Update password in the session if we're editing current
+                       (* Update user in the session if we're editing current
                           user: *)
-                       (if err = [] && passwd <> "" && cur_user.user_login = login
-                        then Session.update_session_password login passwd
-                        else return ()
+                       (if err = [] && cur_user.user_login = login
+                       then 
+                         Db.query_user login >>= function
+                           | Some u -> Session.update_session (User u)
+                           | None -> return_unit (* TODO: what should we do here *)
+                       else return_unit
                        ) >>= fun () ->
                        Session.with_user_login
                          (fun cur_user ->
@@ -243,7 +247,7 @@ let _ =
 
 
 let _ =
-  Eliom_registration.Html.register edit_user_page
+  Eliom_registration.Html.register ~service:edit_user_page
     (fun (caller, editing_login) () ->
        Session.with_user_login
          (fun cur_user ->