@mindful-web/marko-web-identity-x 1.80.0 → 1.80.1

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 CHANGED
@@ -150,6 +150,26 @@ so it bails, writes no `autoSignups`, and **no `subscribe` conversion is ever re
150
150
  `idxAutoSignupCount: 0` seen on every completed One Tap sign-in on overdriveonline.com. Safe to await
151
151
  only because the Omeda client now has a request timeout.
152
152
 
153
+ **The name backfill is non-fatal, and must stay that way.** `GoogleSignInUpdateUser`
154
+ (`updateOwnAppUser`) is the **only** call in this route authenticated as the *user* rather than the
155
+ org — every other one passes `context: { apiToken: getOrgUserApiToken() }` — so it is the only one
156
+ depending on the session token minted moments earlier already being readable. Production shows it
157
+ returning *"The provided value is not a valid token"*, while the same token value works on the very
158
+ next request arriving from the cookie: a read-after-write race on the idx token store. Retried once
159
+ for that reason.
160
+
161
+ Non-fatal matters more than the retry. It backfills a first and last name — cosmetic — but
162
+ `tokenCookie.setTo` has **already run**, so the user is authenticated by the time it executes.
163
+ Throwing turned a fully successful sign-in into a 500, and the client, seeing `!data.ok`, ran `fail()`
164
+ and redirected a *logged-in* user to `/user/login`. A missing display name must never cost someone
165
+ their session. Failures report through `onHookError` and the route continues.
166
+
167
+ `FACET idxNameBackfill` (`ok` | `retried` | `failed`) distinguishes the two bugs that share this one
168
+ error message: if `retried` dominates it is the race; if `failed` dominates the impersonation token
169
+ simply cannot authorize this mutation, and the fix is to stop using it — backfill before impersonation
170
+ via `setUnverifiedAppUserData` (org-token, but writes *unverified* data, so it is a semantic change
171
+ worth deciding deliberately).
172
+
153
173
  `idxGoogleStepMs` records elapsed ms at the start of each step, making the route self-profiling:
154
174
  `FACET idxGoogleStep SELECT max(idxGoogleStepMs)` shows where the ~12s on a *successful* sign-in
155
175
  goes, and a request that dies mid-flight still reports how far it got. Responds `{ ok, requiresUserInput,
@@ -218,7 +238,8 @@ meter / gate.
218
238
  **Telemetry.** Two seams. Neither needs site configuration:
219
239
  - `config.addRequestAttributes` — called with a flat object as the route progresses
220
240
  (`idxGoogleStep`, `idxUserId`, `idxCreatedNewUser`, `idxRequiresUserInput`, `idxGoogleSurface`,
221
- `idxGoogleMissingFieldsBehavior`, `idxAutoSignupCount`, `idxImpersonateAttempts`). **Defaults to `utils/add-custom-attributes.js`**, which tags
241
+ `idxGoogleMissingFieldsBehavior`, `idxAutoSignupCount`, `idxImpersonateAttempts`,
242
+ `idxNameBackfill`, `idxNameBackfillAttempts`). **Defaults to `utils/add-custom-attributes.js`**, which tags
222
243
  the current New Relic transaction, so the attributes ride on the `Transaction` *and* any
223
244
  `TransactionError` it produces — a 500 says which step failed instead of being a bare GraphQL
224
245
  message. That default resolves the agent **only if the app already loaded it** (checks
@@ -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.80.0/components/access.marko",
5
+ marko_componentType = "/@mindful-web/marko-web-identity-x$1.80.1/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.80.0/components/access.marko",
56
+ id: "/@mindful-web/marko-web-identity-x$1.80.1/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.80.0/components/comment-stream.marko",
5
+ marko_componentType = "/@mindful-web/marko-web-identity-x$1.80.1/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.80.0/components/comment-stream.marko",
94
+ id: "/@mindful-web/marko-web-identity-x$1.80.1/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.80.0/components/context.marko",
5
+ marko_componentType = "/@mindful-web/marko-web-identity-x$1.80.1/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.80.0/components/context.marko",
52
+ id: "/@mindful-web/marko-web-identity-x$1.80.1/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.80.0/components/form-access.marko",
5
+ marko_componentType = "/@mindful-web/marko-web-identity-x$1.80.1/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.80.0/components/form-access.marko",
182
+ id: "/@mindful-web/marko-web-identity-x$1.80.1/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.80.0/components/form-authenticate.marko",
5
+ marko_componentType = "/@mindful-web/marko-web-identity-x$1.80.1/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.80.0/components/form-authenticate.marko",
84
+ id: "/@mindful-web/marko-web-identity-x$1.80.1/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.80.0/components/form-change-email.marko",
5
+ marko_componentType = "/@mindful-web/marko-web-identity-x$1.80.1/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.80.0/components/form-change-email.marko",
62
+ id: "/@mindful-web/marko-web-identity-x$1.80.1/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.80.0/components/form-login.marko",
5
+ marko_componentType = "/@mindful-web/marko-web-identity-x$1.80.1/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.80.0/components/form-login.marko",
95
+ id: "/@mindful-web/marko-web-identity-x$1.80.1/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.80.0/components/form-logout.marko",
5
+ marko_componentType = "/@mindful-web/marko-web-identity-x$1.80.1/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.80.0/components/form-logout.marko",
32
+ id: "/@mindful-web/marko-web-identity-x$1.80.1/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.80.0/components/form-profile.marko",
5
+ marko_componentType = "/@mindful-web/marko-web-identity-x$1.80.1/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.80.0/components/form-profile.marko",
87
+ id: "/@mindful-web/marko-web-identity-x$1.80.1/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.80.0/components/form-register.marko",
5
+ marko_componentType = "/@mindful-web/marko-web-identity-x$1.80.1/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.80.0/components/form-register.marko",
28
+ id: "/@mindful-web/marko-web-identity-x$1.80.1/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.80.0/components/google-init.marko",
5
+ marko_componentType = "/@mindful-web/marko-web-identity-x$1.80.1/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.80.0/components/google-init.marko"
242
+ id: "/@mindful-web/marko-web-identity-x$1.80.1/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.80.0/components/google-sign-in-button.marko",
5
+ marko_componentType = "/@mindful-web/marko-web-identity-x$1.80.1/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.80.0/components/google-sign-in-button.marko"
43
+ id: "/@mindful-web/marko-web-identity-x$1.80.1/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.80.0/components/identify.marko",
5
+ marko_componentType = "/@mindful-web/marko-web-identity-x$1.80.1/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.80.0/components/identify.marko",
53
+ id: "/@mindful-web/marko-web-identity-x$1.80.1/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.80.0/components/non-auth-identify.marko",
5
+ marko_componentType = "/@mindful-web/marko-web-identity-x$1.80.1/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.80.0/components/non-auth-identify.marko",
48
+ id: "/@mindful-web/marko-web-identity-x$1.80.1/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.80.0/components/subscribe.marko",
5
+ marko_componentType = "/@mindful-web/marko-web-identity-x$1.80.1/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.80.0/components/subscribe.marko",
76
+ id: "/@mindful-web/marko-web-identity-x$1.80.1/components/subscribe.marko",
77
77
  component: "./subscribe.marko",
78
78
  tags: [
79
79
  "@mindful-web/marko-web/components/browser-component.marko",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mindful-web/marko-web-identity-x",
3
- "version": "1.80.0",
3
+ "version": "1.80.1",
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>",
@@ -38,5 +38,5 @@
38
38
  "publishConfig": {
39
39
  "access": "public"
40
40
  },
41
- "gitHead": "564bbee92cbb8f659f8fc6e7cf8991f8d647dec3"
41
+ "gitHead": "956e3462d636f6e7785c1e82b99d66b4c410e9df"
42
42
  }
package/routes/google.js CHANGED
@@ -33,6 +33,24 @@ const LOGIN_SOURCE = 'google-one-tap';
33
33
  const IMPERSONATE_MAX_ATTEMPTS = 2;
34
34
  const IMPERSONATE_RETRY_DELAY_MS = 250;
35
35
 
36
+ // Same shape as the impersonation retry, and for the same suspected cause: a
37
+ // read-after-write race, this time on the token the previous step just minted.
38
+ const NAME_BACKFILL_MAX_ATTEMPTS = 2;
39
+ const NAME_BACKFILL_RETRY_DELAY_MS = 250;
40
+
41
+ /**
42
+ * A session token that the idx API does not (yet) recognise.
43
+ *
44
+ * Apollo exposes GraphQL errors both individually on `graphQLErrors` and folded
45
+ * into `error.message` as "GraphQL error: <message>"; check both so this does not
46
+ * depend on which shape the caller happens to see.
47
+ */
48
+ const isFreshTokenError = (e) => {
49
+ if (!e) return false;
50
+ const messages = [e.message, ...asArray(e.graphQLErrors).map((g) => g && g.message)];
51
+ return messages.some((m) => /not a valid token|invalid token|token was not found/i.test(String(m)));
52
+ };
53
+
36
54
  /**
37
55
  * Transport-level failures only — a timeout, reset or DNS blip is worth
38
56
  * repeating; a GraphQL validation or authorization error is not, and repeating
@@ -266,10 +284,55 @@ module.exports = asyncRoute(async (req, res) => {
266
284
  if (familyName && !appUser.familyName) nameInput.familyName = familyName;
267
285
  if (Object.keys(nameInput).length) {
268
286
  setStep('update-own-app-user');
269
- await identityX.client.mutate({
270
- mutation: UPDATE_OWN_APP_USER,
271
- variables: { input: nameInput },
272
- });
287
+ // This is the ONLY call in the route authenticated as the *user* rather than
288
+ // the org — every other one passes `context: { apiToken: getOrgUserApiToken() }`
289
+ // — so it is the only one that depends on the session token minted moments
290
+ // earlier already being readable. That is exactly why it is the only
291
+ // operation reported failing: production shows `GoogleSignInUpdateUser`
292
+ // returning "The provided value is not a valid token".
293
+ //
294
+ // The same token value works on the *next* request, arriving from the cookie
295
+ // set above — which is what makes a read-after-write race on the idx token
296
+ // store the leading explanation, so retry once.
297
+ //
298
+ // Non-fatal regardless of the cause, which matters more than the retry. This
299
+ // backfills a first and last name; it is cosmetic. But `tokenCookie.setTo`
300
+ // has already run, so the user IS authenticated by the time we get here —
301
+ // throwing turned a fully successful sign-in into a 500, and the client, on
302
+ // seeing `!data.ok`, ran `fail()` and redirected a logged-in user to
303
+ // /user/login. A missing display name must never cost someone their session.
304
+ let backfill = 'ok';
305
+ let attempts = 0;
306
+ for (;;) {
307
+ attempts += 1;
308
+ try {
309
+ // eslint-disable-next-line no-await-in-loop
310
+ await identityX.client.mutate({
311
+ mutation: UPDATE_OWN_APP_USER,
312
+ variables: { input: nameInput },
313
+ });
314
+ break;
315
+ } catch (e) {
316
+ const retryable = attempts < NAME_BACKFILL_MAX_ATTEMPTS
317
+ && (isFreshTokenError(e) || isRetryableIdxError(e));
318
+ if (!retryable) {
319
+ backfill = 'failed';
320
+ onError(e, {
321
+ idxGoogleStep: 'update-own-app-user',
322
+ idxUserId: appUser.id,
323
+ idxNameBackfillAttempts: attempts,
324
+ });
325
+ break;
326
+ }
327
+ backfill = 'retried';
328
+ // eslint-disable-next-line no-await-in-loop
329
+ await sleep(NAME_BACKFILL_RETRY_DELAY_MS);
330
+ }
331
+ }
332
+ // Measurable: `FACET idxNameBackfill` separates "the retry rescues it" (a
333
+ // race) from "it always fails" (an impersonation token that simply cannot
334
+ // authorize this mutation) — two different bugs with one error message.
335
+ addAttrs({ idxNameBackfill: backfill, idxNameBackfillAttempts: attempts });
273
336
  }
274
337
 
275
338
  // Load the refreshed user so hooks and field checks have up-to-date data.