@mindful-web/marko-web-identity-x 1.80.2 → 1.83.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/CLAUDE.md +20 -3
- package/README.md +18 -0
- package/components/access.marko.js +2 -2
- package/components/comment-stream.marko.js +2 -2
- package/components/context.marko.js +2 -2
- package/components/form-access.marko.js +2 -2
- package/components/form-authenticate.marko.js +2 -2
- package/components/form-change-email.marko.js +2 -2
- package/components/form-login.marko.js +2 -2
- package/components/form-logout.marko.js +2 -2
- package/components/form-profile.marko.js +2 -2
- package/components/form-register.marko.js +2 -2
- package/components/google-init.marko.js +2 -2
- package/components/google-sign-in-button.marko.js +2 -2
- package/components/identify.marko.js +2 -2
- package/components/non-auth-identify.marko.js +2 -2
- package/components/subscribe.marko.js +2 -2
- package/middleware/content-access-state.js +3 -8
- package/middleware/content-download-state.js +4 -8
- package/middleware.js +5 -2
- package/package.json +5 -5
- package/routes/access.js +13 -2
- package/routes/download.js +15 -3
- package/routes/google.js +36 -10
- package/service.js +21 -1
- package/utils/content-form-cookie.js +64 -0
package/CLAUDE.md
CHANGED
|
@@ -184,9 +184,26 @@ simply cannot authorize this mutation, and the fix is to stop using it — backf
|
|
|
184
184
|
via `setUnverifiedAppUserData` (org-token, but writes *unverified* data, so it is a semantic change
|
|
185
185
|
worth deciding deliberately).
|
|
186
186
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
187
|
+
**Timing comes in two parts, deliberately.** `idxGoogleStepMs` is elapsed-at-last-step and
|
|
188
|
+
**last-write-wins** — on a completed request it is effectively total route time, and on one that dies
|
|
189
|
+
mid-flight it says how far in it got. It cannot attribute time to a step: every `setStep` overwrites
|
|
190
|
+
it, which is why the per-step breakdown it was originally meant to provide never actually survived
|
|
191
|
+
(confirmed in production — the query returned one value per transaction, not a profile).
|
|
192
|
+
|
|
193
|
+
`idxMs<Step>` is the real breakdown: one attribute per step (`idxMsVerifyCredential`,
|
|
194
|
+
`idxMsImpersonateAppUser`, `idxMsHooksLoginLinkSent`, `idxMsHooksAuthenticationSuccess`, …), each
|
|
195
|
+
holding how long **that step** took, emitted when the next step begins. `complete` has no duration of
|
|
196
|
+
its own — it is a marker, not work. The durations sum to `idxGoogleStepMs`. About ten attributes, well
|
|
197
|
+
inside New Relic's per-transaction budget.
|
|
198
|
+
|
|
199
|
+
```
|
|
200
|
+
SELECT average(idxMsHooksAuthenticationSuccess), average(idxMsImpersonateAppUser),
|
|
201
|
+
average(idxMsHooksLoginLinkSent), average(idxGoogleStepMs)
|
|
202
|
+
FROM Transaction WHERE name LIKE '%__idx/google%'
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Both hook steps sit on the blocking path and Omeda shows a ~4s tail, so this is what says whether
|
|
206
|
+
they own the remaining latency. Responds `{ ok, requiresUserInput,
|
|
190
207
|
redirectTo, user, entity, additionalEventData, … }`; the client redirects to `/user/profile` when
|
|
191
208
|
input is required.
|
|
192
209
|
|
package/README.md
CHANGED
|
@@ -150,6 +150,24 @@ import IdentityX from '@mindful-web/marko-web-identity-x/browser';
|
|
|
150
150
|
+});
|
|
151
151
|
```
|
|
152
152
|
|
|
153
|
+
## Cookie lifetimes
|
|
154
|
+
|
|
155
|
+
All cookie durations in this package are expressed in **milliseconds**, because that is the unit
|
|
156
|
+
express's `res.cookie` `maxAge` option expects. Env vars and config keys that carry a duration are
|
|
157
|
+
suffixed `_MS` / `Ms` so the unit is unambiguous at the call site.
|
|
158
|
+
|
|
159
|
+
| Variable | Applies to | Default | Notes |
|
|
160
|
+
| - | - | - | - |
|
|
161
|
+
| `IDX_CONTENT_ACCESS_MAXAGE_MS` | `__idx_form_<surveyId>_<contentId>` (content access) | `86400000` (1 day) | |
|
|
162
|
+
| `IDX_CONTENT_DOWNLOAD_MAXAGE_MS` | `__idx_form_content-download_<contentId>` | `86400000` (1 day) | |
|
|
163
|
+
| `IDX_CONTENT_ACCESS_MAXAGE` | — | — | **Deprecated** alias of the `_MS` var above. |
|
|
164
|
+
| `IDX_CONTENT_DOWNLOAD_MAXAGE` | — | — | **Deprecated** alias of the `_MS` var above. |
|
|
165
|
+
|
|
166
|
+
The unsuffixed names are still honored, but avoid them in new deployments: `MAXAGE` reads as the HTTP
|
|
167
|
+
`Max-Age` directive, which is in *seconds*, so a value of `86400` intended as "one day" silently
|
|
168
|
+
produced an 86-second cookie. Non-numeric or non-positive values are ignored with a warning and the
|
|
169
|
+
default applies, rather than sending `NaN` into `res.cookie` (which throws on every affected request).
|
|
170
|
+
|
|
153
171
|
## Vue Event emission
|
|
154
172
|
|
|
155
173
|
This package emits the following events via the EventBus/global Vue root. Each payload will be an
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"use strict";
|
|
3
3
|
|
|
4
4
|
var marko_template = module.exports = require("marko/dist/html").t(__filename),
|
|
5
|
-
marko_componentType = "/@mindful-web/marko-web-identity-x$1.
|
|
5
|
+
marko_componentType = "/@mindful-web/marko-web-identity-x$1.83.0/components/access.marko",
|
|
6
6
|
marko_component = require("./access.marko"),
|
|
7
7
|
marko_renderer = require("marko/dist/runtime/components/renderer"),
|
|
8
8
|
module_objectPath_module = require("@mindful-web/object-path"),
|
|
@@ -53,7 +53,7 @@ marko_template._ = marko_renderer(render, {
|
|
|
53
53
|
}, marko_component);
|
|
54
54
|
|
|
55
55
|
marko_template.meta = {
|
|
56
|
-
id: "/@mindful-web/marko-web-identity-x$1.
|
|
56
|
+
id: "/@mindful-web/marko-web-identity-x$1.83.0/components/access.marko",
|
|
57
57
|
component: "./access.marko",
|
|
58
58
|
tags: [
|
|
59
59
|
"@mindful-web/marko-core/components/resolve.marko"
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"use strict";
|
|
3
3
|
|
|
4
4
|
var marko_template = module.exports = require("marko/dist/html").t(__filename),
|
|
5
|
-
marko_componentType = "/@mindful-web/marko-web-identity-x$1.
|
|
5
|
+
marko_componentType = "/@mindful-web/marko-web-identity-x$1.83.0/components/comment-stream.marko",
|
|
6
6
|
marko_component = require("./comment-stream.marko"),
|
|
7
7
|
marko_renderer = require("marko/dist/runtime/components/renderer"),
|
|
8
8
|
module_objectPath_module = require("@mindful-web/object-path"),
|
|
@@ -91,7 +91,7 @@ marko_template._ = marko_renderer(render, {
|
|
|
91
91
|
}, marko_component);
|
|
92
92
|
|
|
93
93
|
marko_template.meta = {
|
|
94
|
-
id: "/@mindful-web/marko-web-identity-x$1.
|
|
94
|
+
id: "/@mindful-web/marko-web-identity-x$1.83.0/components/comment-stream.marko",
|
|
95
95
|
component: "./comment-stream.marko",
|
|
96
96
|
tags: [
|
|
97
97
|
"@mindful-web/marko-web/components/browser-component.marko",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"use strict";
|
|
3
3
|
|
|
4
4
|
var marko_template = module.exports = require("marko/dist/html").t(__filename),
|
|
5
|
-
marko_componentType = "/@mindful-web/marko-web-identity-x$1.
|
|
5
|
+
marko_componentType = "/@mindful-web/marko-web-identity-x$1.83.0/components/context.marko",
|
|
6
6
|
marko_component = require("./context.marko"),
|
|
7
7
|
marko_renderer = require("marko/dist/runtime/components/renderer"),
|
|
8
8
|
marko_dynamicTag = require("marko/dist/runtime/helpers/dynamic-tag"),
|
|
@@ -49,7 +49,7 @@ marko_template._ = marko_renderer(render, {
|
|
|
49
49
|
}, marko_component);
|
|
50
50
|
|
|
51
51
|
marko_template.meta = {
|
|
52
|
-
id: "/@mindful-web/marko-web-identity-x$1.
|
|
52
|
+
id: "/@mindful-web/marko-web-identity-x$1.83.0/components/context.marko",
|
|
53
53
|
component: "./context.marko",
|
|
54
54
|
tags: [
|
|
55
55
|
"@mindful-web/marko-core/components/resolve.marko"
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"use strict";
|
|
3
3
|
|
|
4
4
|
var marko_template = module.exports = require("marko/dist/html").t(__filename),
|
|
5
|
-
marko_componentType = "/@mindful-web/marko-web-identity-x$1.
|
|
5
|
+
marko_componentType = "/@mindful-web/marko-web-identity-x$1.83.0/components/form-access.marko",
|
|
6
6
|
marko_component = require("./form-access.marko"),
|
|
7
7
|
marko_renderer = require("marko/dist/runtime/components/renderer"),
|
|
8
8
|
module_defaultValue = require("@mindful-web/marko-core/utils/default-value"),
|
|
@@ -179,7 +179,7 @@ marko_template._ = marko_renderer(render, {
|
|
|
179
179
|
}, marko_component);
|
|
180
180
|
|
|
181
181
|
marko_template.meta = {
|
|
182
|
-
id: "/@mindful-web/marko-web-identity-x$1.
|
|
182
|
+
id: "/@mindful-web/marko-web-identity-x$1.83.0/components/form-access.marko",
|
|
183
183
|
component: "./form-access.marko",
|
|
184
184
|
tags: [
|
|
185
185
|
"@mindful-web/marko-web/components/browser-component.marko",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"use strict";
|
|
3
3
|
|
|
4
4
|
var marko_template = module.exports = require("marko/dist/html").t(__filename),
|
|
5
|
-
marko_componentType = "/@mindful-web/marko-web-identity-x$1.
|
|
5
|
+
marko_componentType = "/@mindful-web/marko-web-identity-x$1.83.0/components/form-authenticate.marko",
|
|
6
6
|
marko_component = require("./form-authenticate.marko"),
|
|
7
7
|
marko_renderer = require("marko/dist/runtime/components/renderer"),
|
|
8
8
|
module_objectPath_module = require("@mindful-web/object-path"),
|
|
@@ -81,7 +81,7 @@ marko_template._ = marko_renderer(render, {
|
|
|
81
81
|
}, marko_component);
|
|
82
82
|
|
|
83
83
|
marko_template.meta = {
|
|
84
|
-
id: "/@mindful-web/marko-web-identity-x$1.
|
|
84
|
+
id: "/@mindful-web/marko-web-identity-x$1.83.0/components/form-authenticate.marko",
|
|
85
85
|
component: "./form-authenticate.marko",
|
|
86
86
|
tags: [
|
|
87
87
|
"@mindful-web/marko-web/components/browser-component.marko",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"use strict";
|
|
3
3
|
|
|
4
4
|
var marko_template = module.exports = require("marko/dist/html").t(__filename),
|
|
5
|
-
marko_componentType = "/@mindful-web/marko-web-identity-x$1.
|
|
5
|
+
marko_componentType = "/@mindful-web/marko-web-identity-x$1.83.0/components/form-change-email.marko",
|
|
6
6
|
marko_component = require("./form-change-email.marko"),
|
|
7
7
|
marko_renderer = require("marko/dist/runtime/components/renderer"),
|
|
8
8
|
module_objectPath_module = require("@mindful-web/object-path"),
|
|
@@ -59,7 +59,7 @@ marko_template._ = marko_renderer(render, {
|
|
|
59
59
|
}, marko_component);
|
|
60
60
|
|
|
61
61
|
marko_template.meta = {
|
|
62
|
-
id: "/@mindful-web/marko-web-identity-x$1.
|
|
62
|
+
id: "/@mindful-web/marko-web-identity-x$1.83.0/components/form-change-email.marko",
|
|
63
63
|
component: "./form-change-email.marko",
|
|
64
64
|
tags: [
|
|
65
65
|
"@mindful-web/marko-web/components/browser-component.marko",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"use strict";
|
|
3
3
|
|
|
4
4
|
var marko_template = module.exports = require("marko/dist/html").t(__filename),
|
|
5
|
-
marko_componentType = "/@mindful-web/marko-web-identity-x$1.
|
|
5
|
+
marko_componentType = "/@mindful-web/marko-web-identity-x$1.83.0/components/form-login.marko",
|
|
6
6
|
marko_component = require("./form-login.marko"),
|
|
7
7
|
marko_renderer = require("marko/dist/runtime/components/renderer"),
|
|
8
8
|
module_objectPath_module = require("@mindful-web/object-path"),
|
|
@@ -92,7 +92,7 @@ marko_template._ = marko_renderer(render, {
|
|
|
92
92
|
}, marko_component);
|
|
93
93
|
|
|
94
94
|
marko_template.meta = {
|
|
95
|
-
id: "/@mindful-web/marko-web-identity-x$1.
|
|
95
|
+
id: "/@mindful-web/marko-web-identity-x$1.83.0/components/form-login.marko",
|
|
96
96
|
component: "./form-login.marko",
|
|
97
97
|
tags: [
|
|
98
98
|
"@mindful-web/marko-web/components/browser-component.marko",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"use strict";
|
|
3
3
|
|
|
4
4
|
var marko_template = module.exports = require("marko/dist/html").t(__filename),
|
|
5
|
-
marko_componentType = "/@mindful-web/marko-web-identity-x$1.
|
|
5
|
+
marko_componentType = "/@mindful-web/marko-web-identity-x$1.83.0/components/form-logout.marko",
|
|
6
6
|
marko_renderer = require("marko/dist/runtime/components/renderer"),
|
|
7
7
|
module_defaultValue = require("@mindful-web/marko-core/utils/default-value"),
|
|
8
8
|
defaultValue = module_defaultValue.default || module_defaultValue,
|
|
@@ -29,7 +29,7 @@ marko_template._ = marko_renderer(render, {
|
|
|
29
29
|
});
|
|
30
30
|
|
|
31
31
|
marko_template.meta = {
|
|
32
|
-
id: "/@mindful-web/marko-web-identity-x$1.
|
|
32
|
+
id: "/@mindful-web/marko-web-identity-x$1.83.0/components/form-logout.marko",
|
|
33
33
|
tags: [
|
|
34
34
|
"@mindful-web/marko-web/components/browser-component.marko"
|
|
35
35
|
]
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"use strict";
|
|
3
3
|
|
|
4
4
|
var marko_template = module.exports = require("marko/dist/html").t(__filename),
|
|
5
|
-
marko_componentType = "/@mindful-web/marko-web-identity-x$1.
|
|
5
|
+
marko_componentType = "/@mindful-web/marko-web-identity-x$1.83.0/components/form-profile.marko",
|
|
6
6
|
marko_component = require("./form-profile.marko"),
|
|
7
7
|
marko_renderer = require("marko/dist/runtime/components/renderer"),
|
|
8
8
|
module_objectPath_module = require("@mindful-web/object-path"),
|
|
@@ -84,7 +84,7 @@ marko_template._ = marko_renderer(render, {
|
|
|
84
84
|
}, marko_component);
|
|
85
85
|
|
|
86
86
|
marko_template.meta = {
|
|
87
|
-
id: "/@mindful-web/marko-web-identity-x$1.
|
|
87
|
+
id: "/@mindful-web/marko-web-identity-x$1.83.0/components/form-profile.marko",
|
|
88
88
|
component: "./form-profile.marko",
|
|
89
89
|
tags: [
|
|
90
90
|
"@mindful-web/marko-web/components/browser-component.marko",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"use strict";
|
|
3
3
|
|
|
4
4
|
var marko_template = module.exports = require("marko/dist/html").t(__filename),
|
|
5
|
-
marko_componentType = "/@mindful-web/marko-web-identity-x$1.
|
|
5
|
+
marko_componentType = "/@mindful-web/marko-web-identity-x$1.83.0/components/form-register.marko",
|
|
6
6
|
marko_renderer = require("marko/dist/runtime/components/renderer"),
|
|
7
7
|
marko_assign = require("marko/dist/runtime/helpers/assign"),
|
|
8
8
|
marko_web_identity_x_form_login_template = require("./form-login.marko"),
|
|
@@ -25,7 +25,7 @@ marko_template._ = marko_renderer(render, {
|
|
|
25
25
|
});
|
|
26
26
|
|
|
27
27
|
marko_template.meta = {
|
|
28
|
-
id: "/@mindful-web/marko-web-identity-x$1.
|
|
28
|
+
id: "/@mindful-web/marko-web-identity-x$1.83.0/components/form-register.marko",
|
|
29
29
|
tags: [
|
|
30
30
|
"./form-login.marko"
|
|
31
31
|
]
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"use strict";
|
|
3
3
|
|
|
4
4
|
var marko_template = module.exports = require("marko/dist/html").t(__filename),
|
|
5
|
-
marko_componentType = "/@mindful-web/marko-web-identity-x$1.
|
|
5
|
+
marko_componentType = "/@mindful-web/marko-web-identity-x$1.83.0/components/google-init.marko",
|
|
6
6
|
marko_renderer = require("marko/dist/runtime/components/renderer"),
|
|
7
7
|
marko_attr = require("marko/dist/runtime/html/helpers/attr"),
|
|
8
8
|
marko_str = require("marko/dist/runtime/helpers/to-string");
|
|
@@ -239,5 +239,5 @@ marko_template._ = marko_renderer(render, {
|
|
|
239
239
|
});
|
|
240
240
|
|
|
241
241
|
marko_template.meta = {
|
|
242
|
-
id: "/@mindful-web/marko-web-identity-x$1.
|
|
242
|
+
id: "/@mindful-web/marko-web-identity-x$1.83.0/components/google-init.marko"
|
|
243
243
|
};
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"use strict";
|
|
3
3
|
|
|
4
4
|
var marko_template = module.exports = require("marko/dist/html").t(__filename),
|
|
5
|
-
marko_componentType = "/@mindful-web/marko-web-identity-x$1.
|
|
5
|
+
marko_componentType = "/@mindful-web/marko-web-identity-x$1.83.0/components/google-sign-in-button.marko",
|
|
6
6
|
marko_renderer = require("marko/dist/runtime/components/renderer"),
|
|
7
7
|
helpers_escape_xml = require("marko/dist/runtime/html/helpers/escape-xml"),
|
|
8
8
|
marko_escapeXml = helpers_escape_xml.x,
|
|
@@ -40,5 +40,5 @@ marko_template._ = marko_renderer(render, {
|
|
|
40
40
|
});
|
|
41
41
|
|
|
42
42
|
marko_template.meta = {
|
|
43
|
-
id: "/@mindful-web/marko-web-identity-x$1.
|
|
43
|
+
id: "/@mindful-web/marko-web-identity-x$1.83.0/components/google-sign-in-button.marko"
|
|
44
44
|
};
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"use strict";
|
|
3
3
|
|
|
4
4
|
var marko_template = module.exports = require("marko/dist/html").t(__filename),
|
|
5
|
-
marko_componentType = "/@mindful-web/marko-web-identity-x$1.
|
|
5
|
+
marko_componentType = "/@mindful-web/marko-web-identity-x$1.83.0/components/identify.marko",
|
|
6
6
|
marko_component = require("./identify.marko"),
|
|
7
7
|
marko_renderer = require("marko/dist/runtime/components/renderer"),
|
|
8
8
|
module_objectPath_module = require("@mindful-web/object-path"),
|
|
@@ -50,7 +50,7 @@ marko_template._ = marko_renderer(render, {
|
|
|
50
50
|
}, marko_component);
|
|
51
51
|
|
|
52
52
|
marko_template.meta = {
|
|
53
|
-
id: "/@mindful-web/marko-web-identity-x$1.
|
|
53
|
+
id: "/@mindful-web/marko-web-identity-x$1.83.0/components/identify.marko",
|
|
54
54
|
component: "./identify.marko",
|
|
55
55
|
tags: [
|
|
56
56
|
"@mindful-web/marko-web-gtm/components/push.marko",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"use strict";
|
|
3
3
|
|
|
4
4
|
var marko_template = module.exports = require("marko/dist/html").t(__filename),
|
|
5
|
-
marko_componentType = "/@mindful-web/marko-web-identity-x$1.
|
|
5
|
+
marko_componentType = "/@mindful-web/marko-web-identity-x$1.83.0/components/non-auth-identify.marko",
|
|
6
6
|
marko_component = require("./non-auth-identify.marko"),
|
|
7
7
|
marko_renderer = require("marko/dist/runtime/components/renderer"),
|
|
8
8
|
marko_dynamicTag = require("marko/dist/runtime/helpers/dynamic-tag"),
|
|
@@ -45,7 +45,7 @@ marko_template._ = marko_renderer(render, {
|
|
|
45
45
|
}, marko_component);
|
|
46
46
|
|
|
47
47
|
marko_template.meta = {
|
|
48
|
-
id: "/@mindful-web/marko-web-identity-x$1.
|
|
48
|
+
id: "/@mindful-web/marko-web-identity-x$1.83.0/components/non-auth-identify.marko",
|
|
49
49
|
component: "./non-auth-identify.marko",
|
|
50
50
|
tags: [
|
|
51
51
|
"@mindful-web/marko-core/components/resolve.marko"
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"use strict";
|
|
3
3
|
|
|
4
4
|
var marko_template = module.exports = require("marko/dist/html").t(__filename),
|
|
5
|
-
marko_componentType = "/@mindful-web/marko-web-identity-x$1.
|
|
5
|
+
marko_componentType = "/@mindful-web/marko-web-identity-x$1.83.0/components/subscribe.marko",
|
|
6
6
|
marko_component = require("./subscribe.marko"),
|
|
7
7
|
marko_renderer = require("marko/dist/runtime/components/renderer"),
|
|
8
8
|
module_objectPath_module = require("@mindful-web/object-path"),
|
|
@@ -73,7 +73,7 @@ marko_template._ = marko_renderer(render, {
|
|
|
73
73
|
}, marko_component);
|
|
74
74
|
|
|
75
75
|
marko_template.meta = {
|
|
76
|
-
id: "/@mindful-web/marko-web-identity-x$1.
|
|
76
|
+
id: "/@mindful-web/marko-web-identity-x$1.83.0/components/subscribe.marko",
|
|
77
77
|
component: "./subscribe.marko",
|
|
78
78
|
tags: [
|
|
79
79
|
"@mindful-web/marko-web/components/browser-component.marko",
|
|
@@ -1,15 +1,10 @@
|
|
|
1
1
|
const { get, getAsObject } = require('@mindful-web/object-path');
|
|
2
|
-
|
|
3
|
-
const cookieNamePrefix = '__idx_form';
|
|
4
|
-
// default is one day
|
|
5
|
-
const maxAge = process.env.IDX_CONTENT_ACCESS_MAXAGE
|
|
6
|
-
? Number(process.env.IDX_CONTENT_ACCESS_MAXAGE)
|
|
7
|
-
: (24 * 60 * 60 * 1000);
|
|
2
|
+
const { accessMaxAge, buildName } = require('../utils/content-form-cookie');
|
|
8
3
|
|
|
9
4
|
const contentAccessState = ({ res, content }) => {
|
|
10
5
|
// Handle setting of contentAccessState Object
|
|
11
6
|
const { surveyType, surveyId } = getAsObject(content, 'gating');
|
|
12
|
-
const cookieName =
|
|
7
|
+
const cookieName = buildName({ surveyId, contentId: content.id });
|
|
13
8
|
|
|
14
9
|
const setFormDisplay = ({ req }) => {
|
|
15
10
|
res.locals.contentAccessState.displayForm = !get(req, `cookies.${cookieName}`);
|
|
@@ -20,7 +15,7 @@ const contentAccessState = ({ res, content }) => {
|
|
|
20
15
|
};
|
|
21
16
|
|
|
22
17
|
if (surveyType === 'idx') {
|
|
23
|
-
const cookie = { name: cookieName, maxAge };
|
|
18
|
+
const cookie = { name: cookieName, maxAge: accessMaxAge() };
|
|
24
19
|
res.locals.contentAccessState.formId = surveyId;
|
|
25
20
|
res.locals.contentAccessState.cookie = cookie;
|
|
26
21
|
}
|
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
const { get } = require('@mindful-web/object-path');
|
|
2
|
-
|
|
3
|
-
const cookieNamePrefix = '__idx_form';
|
|
4
|
-
const maxAge = process.env.IDX_CONTENT_DOWNLOAD_MAXAGE
|
|
5
|
-
? Number(process.env.IDX_CONTENT_DOWNLOAD_MAXAGE)
|
|
6
|
-
: (24 * 60 * 60 * 1000);
|
|
2
|
+
const { DOWNLOAD_SURVEY_ID, buildName, downloadMaxAge } = require('../utils/content-form-cookie');
|
|
7
3
|
|
|
8
4
|
const contentDownloadState = ({ res, content }) => {
|
|
9
5
|
// Handle setting of contentDownloadState Object
|
|
10
|
-
const surveyId =
|
|
11
|
-
const cookieName =
|
|
6
|
+
const surveyId = DOWNLOAD_SURVEY_ID;
|
|
7
|
+
const cookieName = buildName({ surveyId, contentId: content.id });
|
|
12
8
|
|
|
13
9
|
const setFormDisplay = ({ req }) => {
|
|
14
10
|
res.locals.contentDownloadState.displayForm = !get(req, `cookies.${cookieName}`);
|
|
@@ -17,7 +13,7 @@ const contentDownloadState = ({ res, content }) => {
|
|
|
17
13
|
displayForm: false,
|
|
18
14
|
setFormDisplay,
|
|
19
15
|
};
|
|
20
|
-
const cookie = { name: cookieName, maxAge };
|
|
16
|
+
const cookie = { name: cookieName, maxAge: downloadMaxAge() };
|
|
21
17
|
res.locals.contentDownloadState.formId = surveyId;
|
|
22
18
|
res.locals.contentDownloadState.cookie = cookie;
|
|
23
19
|
};
|
package/middleware.js
CHANGED
|
@@ -17,8 +17,11 @@ module.exports = (config) => asyncRoute(async (req, res, next) => {
|
|
|
17
17
|
|
|
18
18
|
const cookie = service.getIdentity(res);
|
|
19
19
|
|
|
20
|
-
// Don't overwrite an existing cookie
|
|
21
|
-
if (cookie)
|
|
20
|
+
// Don't overwrite an existing cookie, but do extend its expiration.
|
|
21
|
+
if (cookie) {
|
|
22
|
+
service.refreshIdentityCookie();
|
|
23
|
+
return next();
|
|
24
|
+
}
|
|
22
25
|
|
|
23
26
|
// Set cookie for logged in users
|
|
24
27
|
if (service.token) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mindful-web/marko-web-identity-x",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.83.0",
|
|
4
4
|
"description": "Marko Wrapper for IdentityX",
|
|
5
5
|
"repository": "https://github.com/parameter1/mindful-web/tree/main/packages/marko-web-identity-x",
|
|
6
6
|
"author": "Josh Worden <josh@parameter1.com>",
|
|
@@ -13,9 +13,9 @@
|
|
|
13
13
|
"test": "yarn compile --no-clean && yarn lint"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@mindful-web/marko-web-gtm": "^1.
|
|
17
|
-
"@mindful-web/object-path": "^1.
|
|
18
|
-
"@mindful-web/utils": "^1.
|
|
16
|
+
"@mindful-web/marko-web-gtm": "^1.83.0",
|
|
17
|
+
"@mindful-web/object-path": "^1.83.0",
|
|
18
|
+
"@mindful-web/utils": "^1.83.0",
|
|
19
19
|
"apollo-cache-inmemory": "^1.6.6",
|
|
20
20
|
"apollo-client": "^2.6.10",
|
|
21
21
|
"apollo-link-context": "^1.0.20",
|
|
@@ -38,5 +38,5 @@
|
|
|
38
38
|
"publishConfig": {
|
|
39
39
|
"access": "public"
|
|
40
40
|
},
|
|
41
|
-
"gitHead": "
|
|
41
|
+
"gitHead": "2b694183f136fdd763ae0d1bc47843c5bf6e9290"
|
|
42
42
|
}
|
package/routes/access.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const gql = require('graphql-tag');
|
|
2
2
|
const { asyncRoute } = require('@mindful-web/utils');
|
|
3
|
+
const { accessMaxAge, isValidName } = require('../utils/content-form-cookie');
|
|
3
4
|
|
|
4
5
|
const mutation = gql`
|
|
5
6
|
mutation StoreContentAccessSubmission($input: CreateContentAccessSubmissionMutationInput!) {
|
|
@@ -13,14 +14,24 @@ module.exports = asyncRoute(async (req, res) => {
|
|
|
13
14
|
/** @type {import('../middleware').IdentityXRequest} */
|
|
14
15
|
const { body, apollo, identityX } = req;
|
|
15
16
|
const { contentId, payload, cookie } = body;
|
|
17
|
+
|
|
18
|
+
// Validate BEFORE the mutation: this previously destructured `cookie` blindly and passed its
|
|
19
|
+
// `maxAge` straight to `res.cookie`, so a missing key or non-numeric max age threw a 500 *after*
|
|
20
|
+
// the submission had already committed. The cookie's lifetime is never taken from the request --
|
|
21
|
+
// only its name, which must match one this app would have issued for this content.
|
|
22
|
+
const COOKIE_NAME = cookie && cookie.name;
|
|
23
|
+
if (!isValidName(COOKIE_NAME, contentId)) {
|
|
24
|
+
res.status(400).json({ ok: false, message: 'A valid content access cookie name is required.' });
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
16
28
|
const input = {
|
|
17
29
|
contentId,
|
|
18
30
|
payload,
|
|
19
31
|
ipAddress: req.ip,
|
|
20
32
|
};
|
|
21
|
-
const { name: COOKIE_NAME, maxAge } = cookie;
|
|
22
33
|
await apollo.mutate({ mutation, variables: { input } });
|
|
23
|
-
res.cookie(COOKIE_NAME, true, { maxAge, httpOnly: false });
|
|
34
|
+
res.cookie(COOKIE_NAME, true, { maxAge: accessMaxAge(), httpOnly: false });
|
|
24
35
|
const entity = await identityX.generateEntityId();
|
|
25
36
|
res.json({
|
|
26
37
|
ok: true,
|
package/routes/download.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const gql = require('graphql-tag');
|
|
2
2
|
const { asyncRoute } = require('@mindful-web/utils');
|
|
3
|
+
const { DOWNLOAD_SURVEY_ID, buildName, downloadMaxAge } = require('../utils/content-form-cookie');
|
|
3
4
|
|
|
4
5
|
const mutation = gql`
|
|
5
6
|
mutation StoreContentDownloadSubmission($input: CreateContentDownloadSubmissionMutationInput!) {
|
|
@@ -12,15 +13,26 @@ const mutation = gql`
|
|
|
12
13
|
module.exports = asyncRoute(async (req, res) => {
|
|
13
14
|
/** @type {import('../middleware').IdentityXRequest} */
|
|
14
15
|
const { body, apollo, identityX } = req;
|
|
15
|
-
const { contentId, payload
|
|
16
|
+
const { contentId, payload } = body;
|
|
17
|
+
|
|
18
|
+
// Validate BEFORE the mutation: this previously destructured `cookie` from the body blindly and
|
|
19
|
+
// passed its `maxAge` straight to `res.cookie`, so a missing key or non-numeric max age threw a
|
|
20
|
+
// 500 *after* the submission had already committed. Unlike the access form, the download form's
|
|
21
|
+
// survey id is a fixed constant, so the cookie is derived entirely server-side and the body's
|
|
22
|
+
// `cookie` is ignored.
|
|
23
|
+
if (!contentId) {
|
|
24
|
+
res.status(400).json({ ok: false, message: 'A content id is required.' });
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
16
28
|
const input = {
|
|
17
29
|
contentId,
|
|
18
30
|
payload,
|
|
19
31
|
ipAddress: req.ip,
|
|
20
32
|
};
|
|
21
|
-
const { name: COOKIE_NAME, maxAge } = cookie;
|
|
22
33
|
await apollo.mutate({ mutation, variables: { input } });
|
|
23
|
-
|
|
34
|
+
const COOKIE_NAME = buildName({ surveyId: DOWNLOAD_SURVEY_ID, contentId });
|
|
35
|
+
res.cookie(COOKIE_NAME, true, { maxAge: downloadMaxAge(), httpOnly: false });
|
|
24
36
|
const entity = await identityX.generateEntityId();
|
|
25
37
|
res.json({
|
|
26
38
|
ok: true,
|
package/routes/google.js
CHANGED
|
@@ -105,18 +105,44 @@ module.exports = asyncRoute(async (req, res) => {
|
|
|
105
105
|
// the request dies is the step that failed, so an error arrives already
|
|
106
106
|
// localized instead of being a bare GraphQL message to reverse-engineer.
|
|
107
107
|
//
|
|
108
|
-
//
|
|
109
|
-
//
|
|
110
|
-
//
|
|
111
|
-
// it
|
|
112
|
-
//
|
|
113
|
-
//
|
|
108
|
+
// Timing comes in two parts, because they answer different questions.
|
|
109
|
+
//
|
|
110
|
+
// `idxGoogleStepMs` is elapsed-at-last-step: last-write-wins, so on a completed
|
|
111
|
+
// request it is effectively total route time, and on one that dies mid-flight it
|
|
112
|
+
// says how far in it got. Useful, but it cannot attribute time to a *step* —
|
|
113
|
+
// every `setStep` overwrites the previous value, so the breakdown it was
|
|
114
|
+
// originally meant to provide never survived.
|
|
115
|
+
//
|
|
116
|
+
// `idxMs<Step>` fixes that: one attribute per step, holding how long that step
|
|
117
|
+
// took. Emitted when the *next* step starts, since that is when the previous
|
|
118
|
+
// one is known to be over. `complete` therefore has no duration of its own,
|
|
119
|
+
// which is correct — it is a marker, not work. Roughly ten attributes, well
|
|
120
|
+
// inside New Relic's per-transaction custom attribute budget.
|
|
121
|
+
//
|
|
122
|
+
// SELECT average(idxMsHooksAuthenticationSuccess), average(idxMsImpersonateAppUser),
|
|
123
|
+
// average(idxMsHooksLoginLinkSent)
|
|
124
|
+
// FROM Transaction WHERE name LIKE '%__idx/google%'
|
|
125
|
+
//
|
|
126
|
+
// With Omeda calls showing a ~4s tail and both hook steps sitting on the
|
|
127
|
+
// blocking path, this is what says whether they own the remaining latency.
|
|
114
128
|
const startedAt = Date.now();
|
|
115
129
|
const addAttrs = config.get('addRequestAttributes') || (() => {});
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
130
|
+
|
|
131
|
+
// 'hooks-login-link-sent' -> 'idxMsHooksLoginLinkSent'
|
|
132
|
+
const attrNameForStep = (step) => `idxMs${step.split('-').map((part) => (
|
|
133
|
+
part.charAt(0).toUpperCase() + part.slice(1)
|
|
134
|
+
)).join('')}`;
|
|
135
|
+
|
|
136
|
+
let currentStep;
|
|
137
|
+
let currentStepStartedAt = startedAt;
|
|
138
|
+
const setStep = (step) => {
|
|
139
|
+
const now = Date.now();
|
|
140
|
+
// Close out the step being left, not the one being entered.
|
|
141
|
+
if (currentStep) addAttrs({ [attrNameForStep(currentStep)]: now - currentStepStartedAt });
|
|
142
|
+
currentStep = step;
|
|
143
|
+
currentStepStartedAt = now;
|
|
144
|
+
addAttrs({ idxGoogleStep: step, idxGoogleStepMs: now - startedAt });
|
|
145
|
+
};
|
|
120
146
|
|
|
121
147
|
const { clientId, missingFieldsBehavior = 'profile-gate' } = config.getAsObject('googleAuth');
|
|
122
148
|
// `surface` is the GIS `select_by` bucket ('button' | 'prompt'), sent by the
|
package/service.js
CHANGED
|
@@ -24,6 +24,9 @@ const tokenCookie = require('./utils/token-cookie');
|
|
|
24
24
|
const isEmpty = (v) => v == null || v === '';
|
|
25
25
|
const isFn = (v) => typeof v === 'function';
|
|
26
26
|
const IDENTITY_COOKIE_NAME = '__idx_idt';
|
|
27
|
+
// Express's `res.cookie` maxAge is in **milliseconds**. This was previously expressed in seconds,
|
|
28
|
+
// which silently reduced the cookie's lifetime from one year to ~8.76 hours.
|
|
29
|
+
const IDENTITY_COOKIE_MAX_AGE = 1000 * 60 * 60 * 24 * 365;
|
|
27
30
|
|
|
28
31
|
const { log } = console;
|
|
29
32
|
|
|
@@ -294,12 +297,29 @@ class IdentityX {
|
|
|
294
297
|
*/
|
|
295
298
|
setIdentityCookie(id) {
|
|
296
299
|
const options = {
|
|
297
|
-
maxAge:
|
|
300
|
+
maxAge: IDENTITY_COOKIE_MAX_AGE,
|
|
298
301
|
httpOnly: false,
|
|
299
302
|
};
|
|
300
303
|
this.res.cookie(IDENTITY_COOKIE_NAME, id, options);
|
|
301
304
|
}
|
|
302
305
|
|
|
306
|
+
/**
|
|
307
|
+
* Extends the expiration of an already-persisted identity cookie, so an active visitor's
|
|
308
|
+
* identity only lapses after a genuine year of absence.
|
|
309
|
+
*
|
|
310
|
+
* Deliberately reads `req.cookies` directly rather than using `getIdentity`: that falls back to
|
|
311
|
+
* parsing the outgoing `Set-Cookie` header, and express *appends* cookies rather than replacing
|
|
312
|
+
* them, so refreshing off it would emit a duplicate for a cookie set earlier in this request.
|
|
313
|
+
*
|
|
314
|
+
* @returns {Boolean} Whether the cookie was refreshed.
|
|
315
|
+
*/
|
|
316
|
+
refreshIdentityCookie() {
|
|
317
|
+
const id = get(this.req, `cookies.${IDENTITY_COOKIE_NAME}`);
|
|
318
|
+
if (!id || this.res.headersSent) return false;
|
|
319
|
+
this.setIdentityCookie(id);
|
|
320
|
+
return true;
|
|
321
|
+
}
|
|
322
|
+
|
|
303
323
|
/**
|
|
304
324
|
* @param {Object} o
|
|
305
325
|
* @param {String} o.identifier
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
const { resolveDurationMS } = require('@mindful-web/utils');
|
|
2
|
+
|
|
3
|
+
const COOKIE_NAME_PREFIX = '__idx_form';
|
|
4
|
+
const DEFAULT_MAX_AGE = 24 * 60 * 60 * 1000; // 1 day, in ms (express maxAge is ms)
|
|
5
|
+
const DOWNLOAD_SURVEY_ID = 'content-download';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Resolves the content-access cookie max age, in milliseconds.
|
|
9
|
+
*
|
|
10
|
+
* `IDX_CONTENT_ACCESS_MAXAGE` is a deprecated alias: "MAXAGE" reads as the HTTP `Max-Age`
|
|
11
|
+
* directive, which is in *seconds*, so an operator setting `86400` for "one day" silently got 86
|
|
12
|
+
* seconds. The `_MS` suffix makes the unit explicit; the old name still works.
|
|
13
|
+
*/
|
|
14
|
+
const accessMaxAge = () => resolveDurationMS({
|
|
15
|
+
value: process.env.IDX_CONTENT_ACCESS_MAXAGE_MS || process.env.IDX_CONTENT_ACCESS_MAXAGE,
|
|
16
|
+
defaultValue: DEFAULT_MAX_AGE,
|
|
17
|
+
name: 'IDX_CONTENT_ACCESS_MAXAGE_MS',
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Resolves the content-download cookie max age, in milliseconds. See `accessMaxAge` on the
|
|
22
|
+
* deprecated `IDX_CONTENT_DOWNLOAD_MAXAGE` alias.
|
|
23
|
+
*/
|
|
24
|
+
const downloadMaxAge = () => resolveDurationMS({
|
|
25
|
+
value: process.env.IDX_CONTENT_DOWNLOAD_MAXAGE_MS || process.env.IDX_CONTENT_DOWNLOAD_MAXAGE,
|
|
26
|
+
defaultValue: DEFAULT_MAX_AGE,
|
|
27
|
+
name: 'IDX_CONTENT_DOWNLOAD_MAXAGE_MS',
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @param {Object} params
|
|
32
|
+
* @param {String} params.surveyId
|
|
33
|
+
* @param {String|Number} params.contentId
|
|
34
|
+
* @returns {String} The cookie name, e.g. `__idx_form_content-download_12345`
|
|
35
|
+
*/
|
|
36
|
+
const buildName = ({ surveyId, contentId }) => `${COOKIE_NAME_PREFIX}_${surveyId}_${contentId}`;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Whether a client-supplied cookie name is one this app would have issued for `contentId`.
|
|
40
|
+
*
|
|
41
|
+
* The access form's cookie name is derived from the content's `gating.surveyId`, which the submit
|
|
42
|
+
* route does not have (only `contentId` is posted), so the name still round-trips through the
|
|
43
|
+
* browser. Validating it against the expected shape prevents a caller from writing an
|
|
44
|
+
* arbitrarily-named cookie on `/`, without costing an extra GraphQL round trip on the submit path.
|
|
45
|
+
*
|
|
46
|
+
* @param {String} name
|
|
47
|
+
* @param {String|Number} contentId
|
|
48
|
+
* @returns {Boolean}
|
|
49
|
+
*/
|
|
50
|
+
const isValidName = (name, contentId) => {
|
|
51
|
+
if (typeof name !== 'string' || !contentId) return false;
|
|
52
|
+
const escaped = `${contentId}`.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
53
|
+
return new RegExp(`^${COOKIE_NAME_PREFIX}_[\\w-]+_${escaped}$`).test(name);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
module.exports = {
|
|
57
|
+
COOKIE_NAME_PREFIX,
|
|
58
|
+
DEFAULT_MAX_AGE,
|
|
59
|
+
DOWNLOAD_SURVEY_ID,
|
|
60
|
+
accessMaxAge,
|
|
61
|
+
buildName,
|
|
62
|
+
downloadMaxAge,
|
|
63
|
+
isValidName,
|
|
64
|
+
};
|