Fix to expose openssl dependency
[platform/upstream/libwebsockets.git] / README.generic-sessions.md
1 Notes about generic-sessions Plugin
2 ===================================
3
4 @section gseb Enabling lwsgs for build
5
6 Enable at CMake with -DLWS_WITH_GENERIC_SESSIONS=1
7
8 This also needs sqlite3 (libsqlite3-dev or similar package)
9
10
11 @section gsi lwsgs Introduction
12
13 The generic-sessions protocol plugin provides cookie-based login
14 authentication for lws web and ws connections.
15
16 The plugin handles everything about generic account registration,
17 email verification, lost password, account deletion, and other generic account
18 management.
19
20 Other code, in another eg, ws protocol handler, only needs very high-level
21 state information from generic-sessions, ie, which user the client is
22 authenticated as.  Everything underneath is managed in generic-sessions.
23
24
25  - random 20-byte session id managed in a cookie
26
27  - all information related to the session held at the server, nothing managed clientside
28
29  - sqlite3 used at the server to manage active sessions and users
30
31  - defaults to creating anonymous sessions with no user associated
32
33  - admin account (with user-selectable username) is defined in config with a SHA-1 of the password; rest of the accounts are in sqlite3
34  
35  - user account passwords stored as salted SHA-1 with additional confounder
36   only stored in the JSON config, not the database 
37
38  - login, logout, register account + email verification built-in with examples
39
40  - in a mount, some file suffixes (ie, .js) can be associated with a protocol for the purposes of rewriting symbolnames.  These are read-only copies of logged-in server state.
41
42  - When your page fetches .js or other rewritten files from that mount, "$lwsgs_user" and so on are rewritten on the fly using chunked transfer encoding
43
44  - Eliminates server-side scripting with a few rewritten symbols and
45  javascript on client side
46
47  - 32-bit bitfield for authentication sectoring, mounts can provide a mask on the loggin-in session's associated server-side bitfield that must be set for access.
48
49  - No code (just config) required for, eg, private URL namespace that requires login to access. 
50  
51
52 @section gsin Lwsgs Integration to HTML
53
54 Only three steps are needed to integrate lwsgs in your HTML.
55
56 1) lwsgs HTML UI is bundled with the javascript it uses in `lwsgs.js`, so
57 import that script file in your head section
58
59 2) define an empty div of id "lwsgs" somewhere
60
61 3) Call lwsgs_initial() in your page
62
63 That's it.  An example is below
64
65 ```
66         <html>
67          <head>
68           <script src="lwsgs.js"></script>
69           <style>
70              .body { font-size: 12 }
71              .gstitle { font-size: 18 }
72           </style>
73           </head>
74           <body style="background-image:url(seats.jpg)">
75             <table style="width:100%;transition: max-height 2s;">
76              <tr>
77               <td style="vertical-align:top;text-align:left;width=200px">
78                <img src="lwsgs-logo.png">
79               </td>
80               <td style="vertical-align:top;float:right">
81                 <div id=lwsgs style="text-align:right;background-color: rgba(255, 255, 255, 0.8);"></div>
82               </td>
83              </tr>
84             </table>
85            </form>
86            
87            <script>lwsgs_initial();</script>
88         
89          </body>
90         </html>
91 ```
92
93 @section gsof Lwsgs Overall Flow@
94
95 When the protocol is initialized, it gets per-vhost information from the config, such
96 as where the sqlite3 databases are to be stored.  The admin username and sha-1 of the
97 admin password are also taken from here.
98
99 In the mounts using protocol-generic-sessions, a cookie is maintained against any requests; if no cookie was active on the initial request a new session is
100 created with no attached user.
101
102 So there should always be an active session after any transactions with the server.
103
104 In the example html going to the mount /lwsgs loads a login / register page as the default.
105
106 The <form> in the login page contains 'next url' hidden inputs that let the html 'program' where the form handler will go after a successful admin login, a successful user login and a failed login.
107
108 After a successful login, the sqlite record at the server for the current session is updated to have the logged-in username associated with it. 
109
110
111
112 @section gsconf Lwsgs Configuration
113
114 "auth-mask" defines the authorization sector bits that must be enabled on the session to gain access.
115
116 "auth-mask" 0 is the default.
117
118   - b0 is set if you are logged in as a user at all.
119   - b1 is set if you are logged in with the user configured to be admin
120   - b2 is set if the account has been verified (the account configured for admin is always verified)
121   - b3 is set if your session just did the forgot password flow successfully
122
123 ```
124               {
125                 # things in here can always be served
126                 "mountpoint": "/lwsgs",
127                 "origin": "file:///usr/share/libwebsockets-test-server/generic-sessions",
128                 "origin": "callback://protocol-lws-messageboard",
129                 "default": "generic-sessions-login-example.html",
130                 "auth-mask": "0",
131                 "interpret": {
132                         ".js": "protocol-lws-messageboard"
133                 }
134                }, {
135                 # things in here can only be served if logged in as a user
136                 "mountpoint": "/lwsgs/needauth",
137                 "origin": "file:///usr/share/libwebsockets-test-server/generic-sessions/needauth",
138                 "origin": "callback://protocol-lws-messageboard",
139                 "default": "generic-sessions-login-example.html",
140                 "auth-mask": "5", # logged in as a verified user
141                 "interpret": {
142                         ".js": "protocol-lws-messageboard"
143                 }
144                }, {
145                 # things in here can only be served if logged in as admin
146                 "mountpoint": "/lwsgs/needadmin",
147                 "origin": "file:///usr/share/libwebsockets-test-server/generic-sessions/needadmin",
148                 "origin": "callback://protocol-lws-messageboard",
149                 "default": "generic-sessions-login-example.html",
150                 "auth-mask": "7", # b2 = verified (by email / or admin), b1 = admin, b0 = logged in with any user name
151                 "interpret": {
152                         ".js": "protocol-lws-messageboard"
153                 }
154                }
155 ```
156 Note that the name of the real application protocol that uses generic-sessions
157 is used, not generic-sessions itself. 
158
159 The vhost configures the storage dir, admin credentials and session cookie lifetimes:
160
161 ```
162              "ws-protocols": [{
163                "protocol-generic-sessions": {
164                  "status": "ok",
165                  "admin-user": "admin",
166         
167         # create the pw hash like this (for the example pw, "jipdocesExunt" )
168         # $ echo -n "jipdocesExunt" | sha1sum
169         # 046ce9a9cca769e85798133be06ef30c9c0122c9 -
170         #
171         # Obviously ** change this password hash to a secret one before deploying **
172         #
173                  "admin-password-sha1": "046ce9a9cca769e85798133be06ef30c9c0122c9",
174                  "session-db": "/var/www/sessions/lws.sqlite3",
175                  "timeout-idle-secs": "600",
176                  "timeout-anon-idle-secs": "1200",
177                  "timeout-absolute-secs": "6000",
178         # the confounder is part of the salted password hashes.  If this config
179         # file is in a 0700 root:root dir, an attacker with apache credentials
180         # will have to get the confounder out of the process image to even try
181         # to guess the password hashes.
182                  "confounder": "Change to <=31 chars of junk",
183         
184                  "email-from": "noreply@example.com",
185                  "email-smtp-ip": "127.0.0.1",
186                  "email-expire": "3600",
187                  "email-helo": "myhost.com",
188                  "email-contact-person": "Set Me <real-person@email.com>",
189                  "email-confirm-url-base": "http://localhost:7681/lwsgs"
190                }
191 ```
192
193 The email- related settings control generation of automatic emails for
194 registration and forgotten password.
195
196  - `email-from`: The email address automatic emails are sent from
197
198  - `email-smtp-ip`: Normally 127.0.0.1, if you have a suitable server on port
199    25 on your lan you can use this instead here.
200
201  - `email-expire`: Seconds that links sent in email will work before being
202    deleted
203
204  - `email-helo`: HELO to use when communicating with your SMTP server
205
206  - `email-contact-person`: mentioned in the automatic emails as a human who can
207    answer questions
208
209  - `email-confirm-url-base`: the URL to start links with in the emails, so the
210    recipient can get back to the web server
211    
212 The real protocol that makes use of generic-sessions must also be listed and
213 any configuration it needs given
214
215 ```
216                "protocol-lws-messageboard": {
217                  "status": "ok",
218                  "message-db": "/var/www/sessions/messageboard.sqlite3"
219                },
220 ```
221
222 Notice the real application uses his own sqlite db, no details about how
223 generic-sessions works or how it stores data are available to it.
224
225
226 @section gspwc Lwsgs Password Confounder
227
228 You can also define a per-vhost confounder shown in the example above, used
229 when aggregating the password with the salt when it is hashed.  Any attacker
230 will also need to get the confounder along with the database, which you can
231 make harder by making the config dir only eneterable / readable by root.
232
233
234 @section gsprep Lwsgs Preparing the db directory
235
236 You will have to prepare the db directory so it's suitable for the lwsws user to use,
237 that usually means apache, eg
238
239 ```
240         # mkdir -p /var/www/sessions
241         # chown root:apache /var/www/sessions
242         # chmod 770 /var/www/sessions
243 ```
244
245 @section gsrmail Lwsgs Email configuration
246
247 lwsgs will can send emails by talking to an SMTP server on localhost:25.  That
248 will usually be sendmail or postfix, you should confirm that works first by
249 itself using the `mail` application to send on it.
250
251 lwsgs has been tested on stock Fedora sendmail and postfix.
252
253
254 @section gsap Lwsgs Integration with another protocol
255
256 lwsgs is designed to provide sessions and accounts in a standalone and generic way.
257
258 But it's not useful by itself, there will always be the actual application who wants
259 to make use of generic-sessions features.
260
261 We provide the "messageboard" plugin as an example of how to integrate with
262 your actual application protocol.
263
264 The basic approach is the 'real' protocol handler (usually a plugin itself)
265 subclasses the generic-sessions plugin and calls through to it by default.
266
267 The "real" protocol handler entirely deals with ws-related stuff itself, since
268 generic-sessions does not use ws.  But for
269
270  - LWS_CALLBACK_HTTP
271  - LWS_CALLBACK_HTTP_BODY
272  - LWS_CALLBACK_HTTP_BODY_COMPLETION
273  - LWS_CALLBACK_HTTP_DROP_PROTOCOL
274  
275 the "real" protocol handler checks if it recognizes the activity (eg, his own
276 POST form URL) and if not, passes stuff through to the generic-sessions protocol callback to handle it.  To simplify matters the real protocol can just pass
277 through any unhandled messages to generic-sessions.
278
279 The "real" protocol can get a pointer to generic-sessions protocol on the
280 same vhost using
281
282 ```
283         vhd->gsp = lws_vhost_name_to_protocol(vhd->vh, "protocol-generic-sessions");
284 ```
285
286 The "real" protocol must also arrange generic-sessions per_session_data in his
287 own per-session allocation.  To allow keeping generic-sessions opaque, the
288 real protocol must allocate that space at runtime, using the pss size
289 the generic-sessions protocol struct exposes
290
291 ```
292         struct per_session_data__myapp {
293                 void *pss_gs;
294         ...
295         
296                 pss->pss_gs = malloc(vhd->gsp->per_session_data_size);
297 ```
298
299 The allocation reserved for generic-sessions is then used as user_space when
300 the real protocol calls through to the generic-sessions callback
301
302 ```
303         vhd->gsp->callback(wsi, reason, &pss->pss_gs, in, len);
304 ```
305
306 In that way the "real" protocol can subclass generic-sessions functionality.
307
308
309 To ease management of these secondary allocations, there are callbacks that
310 occur when a wsi binds to a protocol and when the binding is dropped.  These
311 should be used to malloc and free and kind of per-connection
312 secondary allocations.
313
314 ```
315         case LWS_CALLBACK_HTTP_BIND_PROTOCOL:
316                 if (!pss || pss->pss_gs)
317                         break;
318
319                 pss->pss_gs = malloc(vhd->gsp->per_session_data_size);
320                 if (!pss->pss_gs)
321                         return -1;
322
323                 memset(pss->pss_gs, 0, vhd->gsp->per_session_data_size);
324                 break;
325
326         case LWS_CALLBACK_HTTP_DROP_PROTOCOL:
327                 if (vhd->gsp->callback(wsi, reason, pss ? pss->pss_gs : NULL, in, len))
328                         return -1;
329
330                 if (pss->pss_gs) {
331                         free(pss->pss_gs);
332                         pss->pss_gs = NULL;
333                 }
334                 break;
335 ```
336
337
338 #section gsapsib Getting session-specific information from another protocol
339
340 At least at the time when someone tries to upgrade an http(s) connection to
341 ws(s) with your real protocol, it is necessary to confirm the cookie the http(s)
342 connection has with generic-sessions and find out his username and other info.
343
344 Generic sessions lets another protocol check it again by calling his callback,
345 and lws itself provides a generic session info struct to pass the related data
346
347 ```
348         struct lws_session_info {
349                 char username[32];
350                 char email[100];
351                 char ip[72];
352                 unsigned int mask;
353                 char session[42];
354         };
355
356         struct lws_session_info sinfo;
357         ...
358         vhd->gsp->callback(wsi, LWS_CALLBACK_SESSION_INFO,
359                                    &pss->pss_gs, &sinfo, 0);
360 ```
361
362 After the call to generic-sessions, the results can be
363
364  -  all the strings will be zero-length and .mask zero, there is no usable cookie
365  
366   - only .ip and .session are set: the cookie is OK but no user logged in
367   
368   - all the strings contain information about the logged-in user
369
370 the real protocol can use this to reject attempts to open ws connections from
371 http connections that are not authenticated; afterwards there's no need to
372 check the ws connection auth status again.
373