@ti-engine/web-framework 1.26.0 → 1.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,30 @@
2
2
 
3
3
  This document will contain the list of changes made to the framework. The format is based on the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) specification.
4
4
 
5
+ ## Version 1.27.0
6
+
7
+ Session lifetime. Two defects that together threw a signed-in user out roughly ten minutes after sign-in, however
8
+ hard they were working.
9
+
10
+ * fix(web-server): `cookies.maxAge` was `604800` — seven days expressed in **seconds**, written into a field
11
+ express-session reads as **milliseconds** (`set maxAge(ms)`). Every session therefore lasted 604.8 seconds. The
12
+ value is now `28800000`: eight hours, in the unit the field actually takes.
13
+ * fix(web-server): enable `rolling: true`, making the window slide with use. express-session re-sends the cookie
14
+ only when the session is new, when `rolling` is on, or when the session data itself changed
15
+ (`shouldSetCookie`) — and nothing changes it after sign-in, since `augmentSession` runs once inside
16
+ `regenerateAndSaveSession` and the CSRF handler writes its token only when absent. The limit was therefore
17
+ absolute from sign-in rather than an idle timeout. The store side was never the problem: `SessionStore.touch`
18
+ slid the Redis TTL correctly the whole time, which is precisely why the fault was invisible from the server —
19
+ the browser was dropping an expired cookie the server still considered live. `resave: false` is unchanged, so
20
+ this adds no store writes.
21
+ * feat(config): `TI_WEB_SESSION_IDLE_TIMEOUT` overrides the idle window, in whole **minutes**. The unit is named in
22
+ the variable and converted internally, so the confusion above is not expressible through it. A non-integer or
23
+ non-positive value is ignored, leaving the config value standing — the same posture as `TI_WEB_STATIC_MAX_AGE`.
24
+
25
+ Note for consumers choosing a value: a rolling window is refreshed by requests, and a browser filling in a form
26
+ makes none. Set it longer than the longest uninterrupted form-filling sitting your application expects, or that
27
+ user loses unsaved work when they finally submit.
28
+
5
29
  ## Version 1.26.0
6
30
 
7
31
  * feat(config-management): report stored documents that no longer satisfy their registered schema.
package/README.md CHANGED
@@ -17,6 +17,7 @@ The web server configuration (host, port, TLS, cookies, etc.) is normally provid
17
17
  * `TI_WEB_USE_TLS` (`true`/`false`) toggles in-app TLS. Set `false` when a reverse proxy / ingress terminates TLS.
18
18
  * `TI_WEB_TLS_CERT_PATH` / `TI_WEB_TLS_KEY_PATH` override the TLS certificate/key paths (only used when TLS is enabled).
19
19
  * `TI_WEB_COOKIE_SECRET` sets the session cookie signing secret. Set a stable, private value for durable sessions and multi-replica deployments (otherwise a random per-process value is used).
20
+ * `TI_WEB_SESSION_IDLE_TIMEOUT` (whole minutes) sets how long a signed-in session survives **without activity**, overriding `cookies.maxAge`. The window is rolling: every response re-stamps the cookie, so a session ends only after that long with no request at all. Note that a user typing into a form makes no requests, so set this comfortably longer than the longest form a user fills in one sitting. Defaults to 480 (eight hours).
20
21
  * `TI_WEB_AUTH_METHODS` (comma-separated) **replaces** the enabled authentication methods (`auth.enabledMethods`), e.g. `openid-google` or `local,openid-google`.
21
22
  * `TI_WEB_AUTH_LOCAL_USERS_PATH` overrides the local user directory's file path (`auth.local.usersPath`), which backs `local` sign-in. An explicitly empty value means *no directory*, so every local sign-in is refused. See [Local (username/password) authentication](#local-usernamepassword-authentication).
22
23
  * `TI_WEB_AUTH_ADMINS` (comma-separated) **replaces** the admin allowlist (`auth.admins`). Entries are matched against the session user's user ID, username or email, so an OpenID deployment lists emails. An explicitly empty value means *no admins*.
package/bin/web-server.js CHANGED
@@ -319,6 +319,15 @@ class TiWebServer extends ServiceConsumer {
319
319
  secret: this.serviceConfig.cookies.secret || randomBytes( 32 ).toString( "base64" ),
320
320
  resave: false,
321
321
  saveUninitialized: false,
322
+ // Slide the window with use, rather than expiring a fixed interval after sign-in. Without this,
323
+ // express-session re-sends the cookie only when the session data itself changes
324
+ // (`shouldSetCookie`), and nothing changes it after login — `augmentSession` runs once inside
325
+ // `regenerateAndSaveSession`, and the CSRF handler writes its token only when absent. The cookie
326
+ // was therefore stamped at sign-in and never refreshed: an absolute limit that expired people
327
+ // mid-task however hard they were working. `resave: false` keeps the store write-free; the
328
+ // session's TTL there is kept alive by `SessionStore.touch`, which express-session calls per
329
+ // request.
330
+ rolling: true,
322
331
  cookie: {
323
332
  path: this.serviceConfig.cookies.path,
324
333
  httpOnly: this.serviceConfig.cookies.httpOnly,
@@ -39,7 +39,7 @@
39
39
  "path": "/",
40
40
  "httpOnly": true,
41
41
  "sameSite": "lax",
42
- "maxAge": 604800
42
+ "maxAge": 28800000
43
43
  },
44
44
  "host": "127.0.0.1",
45
45
  "language": "en",
@@ -60,6 +60,17 @@ function applyWebConfigEnvOverrides( config, env = process.env ) {
60
60
  config.cookies = config.cookies || {};
61
61
  config.cookies.secret = env.TI_WEB_COOKIE_SECRET;
62
62
  }
63
+ if ( env.TI_WEB_SESSION_IDLE_TIMEOUT !== undefined ) {
64
+ // MINUTES, because that is the unit a deployment actually reasons in — and because the millisecond field it
65
+ // feeds is what went wrong here in the first place: `604800` was written into `cookies.maxAge` meaning seven
66
+ // days, and express-session read it as 604800 MILLISECONDS, giving every user a ten-minute session. Naming
67
+ // the unit in the variable and converting here keeps that mistake from being expressible.
68
+ const minutes = Number( env.TI_WEB_SESSION_IDLE_TIMEOUT );
69
+ if ( Number.isInteger( minutes ) && minutes > 0 ) {
70
+ config.cookies = config.cookies || {};
71
+ config.cookies.maxAge = minutes * 60 * 1000;
72
+ }
73
+ }
63
74
  if ( env.TI_WEB_AUTH_METHODS !== undefined ) {
64
75
  config.auth = config.auth || {};
65
76
  config.auth.enabledMethods = env.TI_WEB_AUTH_METHODS.split( "," ).map( ( method ) => method.trim() ).filter( ( method ) => method.length > 0 );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ti-engine/web-framework",
3
- "version": "1.26.0",
3
+ "version": "1.27.0",
4
4
  "description": "A web-framework based on the ti-engine. It provides a customizable ready-to-use web-server microservice and a set of tools for creating web applications. NOTICE: This is still a work in progress and the full architecture, design, and functionality are not available!",
5
5
  "keywords": [
6
6
  "ti-engine",