@mindful-web/marko-web-identity-x 1.79.7 → 1.79.9
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 +50 -2
- 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 +78 -16
- package/components/google-init.marko.js +80 -18
- 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/package.json +2 -2
- package/routes/google.js +77 -9
package/CLAUDE.md
CHANGED
|
@@ -105,7 +105,55 @@ conflict-guarded; non-fatal via `config.get('onHookError')`) → `impersonateApp
|
|
|
105
105
|
`onAuthenticationSuccess`, same order/hooks as email-link so Omeda rapid-identify + subscription
|
|
106
106
|
sync run identically. `profile-gate` computes `requiresUserInput` by checking required fields are
|
|
107
107
|
non-empty (deliberately not `forceProfileReVerification`). Responds `{ ok, requiresUserInput,
|
|
108
|
-
redirectTo, user, entity, … }`; the client redirects to `/user/profile` when
|
|
108
|
+
redirectTo, user, entity, additionalEventData, … }`; the client redirects to `/user/profile` when
|
|
109
|
+
input is required.
|
|
110
|
+
|
|
111
|
+
**`additionalEventData` and the auto-signup union.** Tenant Omeda formatters *assign*
|
|
112
|
+
`additionalEventData.autoSignups` in place, and that object is the only channel by which the browser
|
|
113
|
+
learns a subscription was created (see analytics below). Where they assign it varies, and three
|
|
114
|
+
repos do it twice:
|
|
115
|
+
|
|
116
|
+
| Writes `autoSignups` in | Repos |
|
|
117
|
+
| --- | --- |
|
|
118
|
+
| `onLoginLinkSentFormatter` only | ab-media, allured, encore360, pmmi |
|
|
119
|
+
| `onAuthenticationSuccessFormatter` only | fusable, cox-matthews |
|
|
120
|
+
| **Both** | industrial (both pkgs), ironmarkets, watt |
|
|
121
|
+
|
|
122
|
+
So the route gives **each hook its own object** and merges the two afterwards, taking the union of
|
|
123
|
+
`autoSignups` deduped by `productId`. Sharing one reference (the obvious move, and what
|
|
124
|
+
`routes/authenticate.js` does — it only ever calls one hook) would let the second hook's assignment
|
|
125
|
+
clobber the first, silently dropping a whole set of subscribe events on the both-hook repos. In the
|
|
126
|
+
email-link flow those hooks fire on **separate requests** (login vs authenticate) and each reports
|
|
127
|
+
its own set, so the union is what preserves existing behaviour. Do **not** "simplify" this back to a
|
|
128
|
+
shared object.
|
|
129
|
+
|
|
130
|
+
`requiresUserInput` goes to `onAuthenticationSuccess` **only**: the onLoginLinkSent formatters read a
|
|
131
|
+
truthy value as "skip the Omeda subscription lookup" and then opt the user into every configured
|
|
132
|
+
deployment type without deduping, firing a phantom auto-signup for an existing Omeda customer who
|
|
133
|
+
merely lacks an idx required field. The email-link flow never exposes it there either —
|
|
134
|
+
`routes/login.js` 400s before any hook runs.
|
|
135
|
+
|
|
136
|
+
`idxAutoSignupCount` rides on the transaction attributes so "sign-in worked, Omeda updated, GA4
|
|
137
|
+
recorded nothing" — the exact shape of the July 2026 bug — is a visible number rather than something
|
|
138
|
+
a stakeholder finds in a report weeks later.
|
|
139
|
+
|
|
140
|
+
**Analytics parity (`components/google-init.marko`).** The Vue flows get `dataLayer` + p1events for
|
|
141
|
+
free from the EventBus bridge in `browser/index.js`; the One-Tap callback is plain inline JS on a
|
|
142
|
+
`<script>` tag, so it has to emit the same events by hand. Three things it must keep in step with
|
|
143
|
+
`browser/authenticate.vue` + `browser/mixins/*`:
|
|
144
|
+
- **`identity-x-authenticated`** — payload mirrors `global-event-emitter.js`, which sets
|
|
145
|
+
`actionSource`, `loginSource` **and** `source` to the same value. GTM variables in the wild read
|
|
146
|
+
whichever one they were written against; emitting only `actionSource` silently breaks the rest.
|
|
147
|
+
- **`identity-x-auto-signup`** — one event per `additionalEventData.autoSignups` entry, shape
|
|
148
|
+
`{ entity, ...autoSignup, event }` per `global-auto-signup-event-emitter.js`. **This — not
|
|
149
|
+
`identity-x-authenticated` — is what the sites' GA4 "subscribe" conversion triggers on.** The
|
|
150
|
+
Omeda opt-in happens server-side either way, so a missing event here looks like "One Tap doesn't
|
|
151
|
+
subscribe anyone" when the subscription exists and only the tracking is gone. Also mirrored to
|
|
152
|
+
p1events as `Subscription / Subscribe`.
|
|
153
|
+
- **Navigation is deferred ~300ms** after the pushes. The email-link flow navigates in the same tick
|
|
154
|
+
but does so from a fully-loaded `/user/authenticate`; One Tap can resolve far earlier in a page's
|
|
155
|
+
life, before GTM has dispatched. `window.dataLayer` is also **assigned, not guarded** (`= … || []`)
|
|
156
|
+
so a sign-in that beats a deferred container still queues its events instead of dropping them.
|
|
109
157
|
|
|
110
158
|
**Tags** (`components/marko.json`, config read from `req.identityX.config`):
|
|
111
159
|
- `<marko-web-identity-x-google-init>` — loads the GSI client + defines `window.handleGoogleOneTapCredential`;
|
|
@@ -122,7 +170,7 @@ meter / gate.
|
|
|
122
170
|
**Telemetry.** Two seams. Neither needs site configuration:
|
|
123
171
|
- `config.addRequestAttributes` — called with a flat object as the route progresses
|
|
124
172
|
(`idxGoogleStep`, `idxUserId`, `idxCreatedNewUser`, `idxRequiresUserInput`, `idxGoogleSurface`,
|
|
125
|
-
`idxGoogleMissingFieldsBehavior`). **Defaults to `utils/add-custom-attributes.js`**, which tags
|
|
173
|
+
`idxGoogleMissingFieldsBehavior`, `idxAutoSignupCount`). **Defaults to `utils/add-custom-attributes.js`**, which tags
|
|
126
174
|
the current New Relic transaction, so the attributes ride on the `Transaction` *and* any
|
|
127
175
|
`TransactionError` it produces — a 500 says which step failed instead of being a bare GraphQL
|
|
128
176
|
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.79.
|
|
5
|
+
marko_componentType = "/@mindful-web/marko-web-identity-x$1.79.9/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.79.
|
|
56
|
+
id: "/@mindful-web/marko-web-identity-x$1.79.9/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.79.
|
|
5
|
+
marko_componentType = "/@mindful-web/marko-web-identity-x$1.79.9/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.79.
|
|
94
|
+
id: "/@mindful-web/marko-web-identity-x$1.79.9/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.79.
|
|
5
|
+
marko_componentType = "/@mindful-web/marko-web-identity-x$1.79.9/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.79.
|
|
52
|
+
id: "/@mindful-web/marko-web-identity-x$1.79.9/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.79.
|
|
5
|
+
marko_componentType = "/@mindful-web/marko-web-identity-x$1.79.9/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.79.
|
|
182
|
+
id: "/@mindful-web/marko-web-identity-x$1.79.9/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.79.
|
|
5
|
+
marko_componentType = "/@mindful-web/marko-web-identity-x$1.79.9/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.79.
|
|
84
|
+
id: "/@mindful-web/marko-web-identity-x$1.79.9/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.79.
|
|
5
|
+
marko_componentType = "/@mindful-web/marko-web-identity-x$1.79.9/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.79.
|
|
62
|
+
id: "/@mindful-web/marko-web-identity-x$1.79.9/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.79.
|
|
5
|
+
marko_componentType = "/@mindful-web/marko-web-identity-x$1.79.9/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.79.
|
|
95
|
+
id: "/@mindful-web/marko-web-identity-x$1.79.9/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.79.
|
|
5
|
+
marko_componentType = "/@mindful-web/marko-web-identity-x$1.79.9/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.79.
|
|
32
|
+
id: "/@mindful-web/marko-web-identity-x$1.79.9/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.79.
|
|
5
|
+
marko_componentType = "/@mindful-web/marko-web-identity-x$1.79.9/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.79.
|
|
87
|
+
id: "/@mindful-web/marko-web-identity-x$1.79.9/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.79.
|
|
5
|
+
marko_componentType = "/@mindful-web/marko-web-identity-x$1.79.9/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.79.
|
|
28
|
+
id: "/@mindful-web/marko-web-identity-x$1.79.9/components/form-register.marko",
|
|
29
29
|
tags: [
|
|
30
30
|
"./form-login.marko"
|
|
31
31
|
]
|
|
@@ -101,6 +101,9 @@ $ const initScript = `
|
|
|
101
101
|
return;
|
|
102
102
|
}
|
|
103
103
|
authenticated = true;
|
|
104
|
+
var eventData = data.additionalEventData || {};
|
|
105
|
+
var autoSignups = eventData.autoSignups || [];
|
|
106
|
+
|
|
104
107
|
if (window.p1events) {
|
|
105
108
|
// De-anonymize the session to the app user, then record the
|
|
106
109
|
// authentication — mirrors the identity-x conversion events.
|
|
@@ -111,30 +114,89 @@ $ const initScript = `
|
|
|
111
114
|
label: eventLabel,
|
|
112
115
|
props: { idxEntity: data.entity, actionSource: 'google-one-tap' },
|
|
113
116
|
});
|
|
117
|
+
// One 'Subscription / Subscribe' event per newsletter the server
|
|
118
|
+
// opted this user into, matching what browser/index.js emits for the
|
|
119
|
+
// 'identity-x-auto-signup' bus event in the email-link flow.
|
|
120
|
+
for (var pi = 0; pi < autoSignups.length; pi += 1) {
|
|
121
|
+
window.p1events('track', {
|
|
122
|
+
category: 'Subscription',
|
|
123
|
+
action: 'Subscribe',
|
|
124
|
+
props: {
|
|
125
|
+
idxEntity: data.entity,
|
|
126
|
+
actionSource: autoSignups[pi].actionSource || data.loginSource,
|
|
127
|
+
subscriptionEntity: autoSignups[pi].subscriptionEntity,
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
}
|
|
114
131
|
}
|
|
115
|
-
|
|
116
|
-
//
|
|
117
|
-
//
|
|
118
|
-
//
|
|
119
|
-
//
|
|
120
|
-
|
|
121
|
-
|
|
132
|
+
|
|
133
|
+
// Push to dataLayer for GTM/GA4. Assign rather than guard: GTM can be
|
|
134
|
+
// deferred behind the script loader, and a One-Tap sign-in that lands
|
|
135
|
+
// before the container initializes would otherwise drop its events
|
|
136
|
+
// silently. Pushing into a plain array is safe — GTM replays whatever
|
|
137
|
+
// is already there when it loads.
|
|
138
|
+
window.dataLayer = window.dataLayer || [];
|
|
139
|
+
|
|
140
|
+
// Mirror the payload the standard email-link authenticate flow builds
|
|
141
|
+
// (browser/authenticate.vue + mixins/global-event-emitter.js) so GTM
|
|
142
|
+
// triggers and dataLayer variables resolve identically. That flow sets
|
|
143
|
+
// actionSource, loginSource AND source to the same value; a variable
|
|
144
|
+
// reading any one of the three has to work here too.
|
|
145
|
+
var idxAuthPayload = {
|
|
146
|
+
event: 'identity-x-authenticated',
|
|
147
|
+
'identity-x': {
|
|
122
148
|
event: 'identity-x-authenticated',
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
149
|
+
ok: true,
|
|
150
|
+
applicationId: data.applicationId,
|
|
151
|
+
user: data.user,
|
|
152
|
+
id: data.user.id,
|
|
153
|
+
email: data.user.email,
|
|
154
|
+
verifiedCount: data.user.verifiedCount,
|
|
155
|
+
entity: data.entity,
|
|
156
|
+
requiresUserInput: data.requiresUserInput,
|
|
157
|
+
createdNewUser: eventData.createdNewUser,
|
|
158
|
+
additionalEventData: eventData,
|
|
159
|
+
actionSource: data.loginSource,
|
|
160
|
+
loginSource: data.loginSource,
|
|
161
|
+
source: data.loginSource,
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
window.dataLayer.push(idxAuthPayload);
|
|
165
|
+
|
|
166
|
+
// The 'subscribe' conversion keys off 'identity-x-auto-signup', not
|
|
167
|
+
// 'identity-x-authenticated' — one event per newsletter product the
|
|
168
|
+
// Omeda hook subscribed the user to. Shape matches
|
|
169
|
+
// mixins/global-auto-signup-event-emitter.js -> browser/index.js:
|
|
170
|
+
// { entity, ...autoSignup, event }.
|
|
171
|
+
for (var i = 0; i < autoSignups.length; i += 1) {
|
|
172
|
+
var signupData = { entity: data.entity };
|
|
173
|
+
var autoSignup = autoSignups[i];
|
|
174
|
+
for (var key in autoSignup) {
|
|
175
|
+
if (Object.prototype.hasOwnProperty.call(autoSignup, key)) {
|
|
176
|
+
signupData[key] = autoSignup[key];
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
signupData.event = 'identity-x-auto-signup';
|
|
180
|
+
window.dataLayer.push({
|
|
181
|
+
event: 'identity-x-auto-signup',
|
|
182
|
+
'identity-x': signupData,
|
|
130
183
|
});
|
|
131
184
|
}
|
|
185
|
+
|
|
186
|
+
var href;
|
|
132
187
|
if (data.requiresUserInput) {
|
|
133
188
|
var destination = data.redirectTo || window.location.pathname;
|
|
134
|
-
|
|
189
|
+
href = '/user/profile?returnTo=' + encodeURIComponent(destination);
|
|
135
190
|
} else {
|
|
136
|
-
|
|
191
|
+
href = data.redirectTo || window.location.href;
|
|
137
192
|
}
|
|
193
|
+
// Give GTM a moment to dispatch the tags above before tearing down the
|
|
194
|
+
// document. The email-link flow navigates in the same tick, but it does
|
|
195
|
+
// so from /user/authenticate after a full page load, where the container
|
|
196
|
+
// is long since warm; a One-Tap sign-in can resolve much earlier in a
|
|
197
|
+
// page's life. A short delay is imperceptible next to the sign-in
|
|
198
|
+
// round-trip and cheaper than losing the conversion.
|
|
199
|
+
window.setTimeout(function() { window.location.href = href; }, 300);
|
|
138
200
|
})
|
|
139
201
|
.catch(function(e) {
|
|
140
202
|
fail('An error occurred during Google sign-in. Please try again.', e);
|
|
@@ -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.79.
|
|
5
|
+
marko_componentType = "/@mindful-web/marko-web-identity-x$1.79.9/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");
|
|
@@ -118,6 +118,9 @@ function render(input, out, __component, component, state) {
|
|
|
118
118
|
return;
|
|
119
119
|
}
|
|
120
120
|
authenticated = true;
|
|
121
|
+
var eventData = data.additionalEventData || {};
|
|
122
|
+
var autoSignups = eventData.autoSignups || [];
|
|
123
|
+
|
|
121
124
|
if (window.p1events) {
|
|
122
125
|
// De-anonymize the session to the app user, then record the
|
|
123
126
|
// authentication — mirrors the identity-x conversion events.
|
|
@@ -128,30 +131,89 @@ function render(input, out, __component, component, state) {
|
|
|
128
131
|
label: eventLabel,
|
|
129
132
|
props: { idxEntity: data.entity, actionSource: 'google-one-tap' },
|
|
130
133
|
});
|
|
134
|
+
// One 'Subscription / Subscribe' event per newsletter the server
|
|
135
|
+
// opted this user into, matching what browser/index.js emits for the
|
|
136
|
+
// 'identity-x-auto-signup' bus event in the email-link flow.
|
|
137
|
+
for (var pi = 0; pi < autoSignups.length; pi += 1) {
|
|
138
|
+
window.p1events('track', {
|
|
139
|
+
category: 'Subscription',
|
|
140
|
+
action: 'Subscribe',
|
|
141
|
+
props: {
|
|
142
|
+
idxEntity: data.entity,
|
|
143
|
+
actionSource: autoSignups[pi].actionSource || data.loginSource,
|
|
144
|
+
subscriptionEntity: autoSignups[pi].subscriptionEntity,
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
}
|
|
131
148
|
}
|
|
132
|
-
|
|
133
|
-
//
|
|
134
|
-
//
|
|
135
|
-
//
|
|
136
|
-
//
|
|
137
|
-
|
|
138
|
-
|
|
149
|
+
|
|
150
|
+
// Push to dataLayer for GTM/GA4. Assign rather than guard: GTM can be
|
|
151
|
+
// deferred behind the script loader, and a One-Tap sign-in that lands
|
|
152
|
+
// before the container initializes would otherwise drop its events
|
|
153
|
+
// silently. Pushing into a plain array is safe — GTM replays whatever
|
|
154
|
+
// is already there when it loads.
|
|
155
|
+
window.dataLayer = window.dataLayer || [];
|
|
156
|
+
|
|
157
|
+
// Mirror the payload the standard email-link authenticate flow builds
|
|
158
|
+
// (browser/authenticate.vue + mixins/global-event-emitter.js) so GTM
|
|
159
|
+
// triggers and dataLayer variables resolve identically. That flow sets
|
|
160
|
+
// actionSource, loginSource AND source to the same value; a variable
|
|
161
|
+
// reading any one of the three has to work here too.
|
|
162
|
+
var idxAuthPayload = {
|
|
163
|
+
event: 'identity-x-authenticated',
|
|
164
|
+
'identity-x': {
|
|
139
165
|
event: 'identity-x-authenticated',
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
166
|
+
ok: true,
|
|
167
|
+
applicationId: data.applicationId,
|
|
168
|
+
user: data.user,
|
|
169
|
+
id: data.user.id,
|
|
170
|
+
email: data.user.email,
|
|
171
|
+
verifiedCount: data.user.verifiedCount,
|
|
172
|
+
entity: data.entity,
|
|
173
|
+
requiresUserInput: data.requiresUserInput,
|
|
174
|
+
createdNewUser: eventData.createdNewUser,
|
|
175
|
+
additionalEventData: eventData,
|
|
176
|
+
actionSource: data.loginSource,
|
|
177
|
+
loginSource: data.loginSource,
|
|
178
|
+
source: data.loginSource,
|
|
179
|
+
},
|
|
180
|
+
};
|
|
181
|
+
window.dataLayer.push(idxAuthPayload);
|
|
182
|
+
|
|
183
|
+
// The 'subscribe' conversion keys off 'identity-x-auto-signup', not
|
|
184
|
+
// 'identity-x-authenticated' — one event per newsletter product the
|
|
185
|
+
// Omeda hook subscribed the user to. Shape matches
|
|
186
|
+
// mixins/global-auto-signup-event-emitter.js -> browser/index.js:
|
|
187
|
+
// { entity, ...autoSignup, event }.
|
|
188
|
+
for (var i = 0; i < autoSignups.length; i += 1) {
|
|
189
|
+
var signupData = { entity: data.entity };
|
|
190
|
+
var autoSignup = autoSignups[i];
|
|
191
|
+
for (var key in autoSignup) {
|
|
192
|
+
if (Object.prototype.hasOwnProperty.call(autoSignup, key)) {
|
|
193
|
+
signupData[key] = autoSignup[key];
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
signupData.event = 'identity-x-auto-signup';
|
|
197
|
+
window.dataLayer.push({
|
|
198
|
+
event: 'identity-x-auto-signup',
|
|
199
|
+
'identity-x': signupData,
|
|
147
200
|
});
|
|
148
201
|
}
|
|
202
|
+
|
|
203
|
+
var href;
|
|
149
204
|
if (data.requiresUserInput) {
|
|
150
205
|
var destination = data.redirectTo || window.location.pathname;
|
|
151
|
-
|
|
206
|
+
href = '/user/profile?returnTo=' + encodeURIComponent(destination);
|
|
152
207
|
} else {
|
|
153
|
-
|
|
208
|
+
href = data.redirectTo || window.location.href;
|
|
154
209
|
}
|
|
210
|
+
// Give GTM a moment to dispatch the tags above before tearing down the
|
|
211
|
+
// document. The email-link flow navigates in the same tick, but it does
|
|
212
|
+
// so from /user/authenticate after a full page load, where the container
|
|
213
|
+
// is long since warm; a One-Tap sign-in can resolve much earlier in a
|
|
214
|
+
// page's life. A short delay is imperceptible next to the sign-in
|
|
215
|
+
// round-trip and cheaper than losing the conversion.
|
|
216
|
+
window.setTimeout(function() { window.location.href = href; }, 300);
|
|
155
217
|
})
|
|
156
218
|
.catch(function(e) {
|
|
157
219
|
fail('An error occurred during Google sign-in. Please try again.', e);
|
|
@@ -177,5 +239,5 @@ marko_template._ = marko_renderer(render, {
|
|
|
177
239
|
});
|
|
178
240
|
|
|
179
241
|
marko_template.meta = {
|
|
180
|
-
id: "/@mindful-web/marko-web-identity-x$1.79.
|
|
242
|
+
id: "/@mindful-web/marko-web-identity-x$1.79.9/components/google-init.marko"
|
|
181
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.79.
|
|
5
|
+
marko_componentType = "/@mindful-web/marko-web-identity-x$1.79.9/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.79.
|
|
43
|
+
id: "/@mindful-web/marko-web-identity-x$1.79.9/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.79.
|
|
5
|
+
marko_componentType = "/@mindful-web/marko-web-identity-x$1.79.9/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.79.
|
|
53
|
+
id: "/@mindful-web/marko-web-identity-x$1.79.9/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.79.
|
|
5
|
+
marko_componentType = "/@mindful-web/marko-web-identity-x$1.79.9/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.79.
|
|
48
|
+
id: "/@mindful-web/marko-web-identity-x$1.79.9/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.79.
|
|
5
|
+
marko_componentType = "/@mindful-web/marko-web-identity-x$1.79.9/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.79.
|
|
76
|
+
id: "/@mindful-web/marko-web-identity-x$1.79.9/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.79.
|
|
3
|
+
"version": "1.79.9",
|
|
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": "
|
|
41
|
+
"gitHead": "450cfb4efb7f30564f3230912fce04b5fc574a1a"
|
|
42
42
|
}
|
package/routes/google.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const gql = require('graphql-tag');
|
|
2
2
|
const { OAuth2Client } = require('google-auth-library');
|
|
3
|
-
const { asyncRoute } = require('@mindful-web/utils');
|
|
3
|
+
const { asArray, asyncRoute } = require('@mindful-web/utils');
|
|
4
4
|
const IMPERSONATE_APP_USER = require('../api/mutations/impersonate-app-user');
|
|
5
5
|
const tokenCookie = require('../utils/token-cookie');
|
|
6
6
|
const contextCookie = require('../utils/context-cookie');
|
|
@@ -177,13 +177,19 @@ module.exports = asyncRoute(async (req, res) => {
|
|
|
177
177
|
// every subsequent user-authenticated call (see updateOwnAppUser below).
|
|
178
178
|
identityX.setToken(authToken.value);
|
|
179
179
|
|
|
180
|
-
//
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
180
|
+
// Backfill name fields from Google as verified data — blanks only, never
|
|
181
|
+
// overwriting a value the user already has.
|
|
182
|
+
//
|
|
183
|
+
// Building the payload first and gating on it subsumes the previous
|
|
184
|
+
// `needsNameUpdate && (givenName || familyName)` guard, which was satisfiable
|
|
185
|
+
// without either field qualifying: a record holding a given name but no
|
|
186
|
+
// family name, signing in with a Google profile that returns `given_name`
|
|
187
|
+
// and no `family_name`, passed both halves and then sent `input: {}`.
|
|
188
|
+
const nameInput = {};
|
|
189
|
+
if (givenName && !appUser.givenName) nameInput.givenName = givenName;
|
|
190
|
+
if (familyName && !appUser.familyName) nameInput.familyName = familyName;
|
|
191
|
+
if (Object.keys(nameInput).length) {
|
|
192
|
+
setStep('update-own-app-user');
|
|
187
193
|
await identityX.client.mutate({
|
|
188
194
|
mutation: UPDATE_OWN_APP_USER,
|
|
189
195
|
variables: { input: nameInput },
|
|
@@ -208,6 +214,28 @@ module.exports = asyncRoute(async (req, res) => {
|
|
|
208
214
|
}
|
|
209
215
|
addAttrs({ idxRequiresUserInput: requiresUserInput });
|
|
210
216
|
|
|
217
|
+
// Tenant Omeda formatters write `autoSignups` into their `additionalEventData`
|
|
218
|
+
// in place — one entry per newsletter product they opt the user into — and
|
|
219
|
+
// that object is what goes out in the response body. It is the only channel by
|
|
220
|
+
// which the browser learns a subscription was created; without it no
|
|
221
|
+
// `identity-x-auto-signup` event reaches GTM/GA4 (the "subscribe" conversion)
|
|
222
|
+
// or p1events, and One Tap looks like it subscribes nobody even though the
|
|
223
|
+
// Omeda opt-in ran fine.
|
|
224
|
+
//
|
|
225
|
+
// Give each hook its OWN object and merge after, rather than sharing one
|
|
226
|
+
// reference, because tenants disagree about where the write lives AND several
|
|
227
|
+
// write in both places:
|
|
228
|
+
// onLoginLinkSentFormatter only ....... ab-media, allured, encore360, pmmi
|
|
229
|
+
// onAuthenticationSuccessFormatter only fusable, cox-matthews
|
|
230
|
+
// BOTH ................................ industrial, ironmarkets, watt
|
|
231
|
+
// Every tenant *assigns* `autoSignups` rather than appending, so a shared
|
|
232
|
+
// reference would let the second hook clobber the first — silently dropping a
|
|
233
|
+
// whole set of subscribe events for the three both-hook repos. In the
|
|
234
|
+
// email-link flow the two hooks fire on separate requests (login vs
|
|
235
|
+
// authenticate) and each reports its own set, so the union is what preserves
|
|
236
|
+
// existing behaviour here.
|
|
237
|
+
const linkSentEventData = { createdNewUser };
|
|
238
|
+
|
|
211
239
|
// Fire onLoginLinkSent before onAuthenticationSuccess so that hooks wired to
|
|
212
240
|
// it (e.g. Omeda rapid-identify and subscription sync) run for Google users
|
|
213
241
|
// the same way they do for email-link users. This also populates
|
|
@@ -218,12 +246,22 @@ module.exports = asyncRoute(async (req, res) => {
|
|
|
218
246
|
req,
|
|
219
247
|
source: LOGIN_SOURCE,
|
|
220
248
|
user,
|
|
249
|
+
additionalEventData: linkSentEventData,
|
|
221
250
|
});
|
|
222
251
|
|
|
223
252
|
// Re-fetch so onAuthenticationSuccess sees the externalIds written by
|
|
224
253
|
// the onLoginLinkSent hook (Omeda rapid-identify).
|
|
225
254
|
const userAfterLinkSent = await identityX.findUserById(appUser.id);
|
|
226
255
|
|
|
256
|
+
// `requiresUserInput` is exposed to this hook only. The onLoginLinkSent
|
|
257
|
+
// formatters read a truthy value as "skip the Omeda subscription lookup" and
|
|
258
|
+
// then opt the user into every configured deployment type without deduping
|
|
259
|
+
// against what they already hold — a phantom auto-signup, and an inflated GA4
|
|
260
|
+
// subscribe count, for an existing Omeda customer who merely lacks an idx
|
|
261
|
+
// required field. The email-link flow never exposes it there either:
|
|
262
|
+
// `routes/login.js` 400s with `requiresUserInput` before any hook runs.
|
|
263
|
+
const authEventData = { createdNewUser, requiresUserInput };
|
|
264
|
+
|
|
227
265
|
setStep('hooks-authentication-success');
|
|
228
266
|
await callHooksFor(identityX, 'onAuthenticationSuccess', {
|
|
229
267
|
req,
|
|
@@ -231,9 +269,38 @@ module.exports = asyncRoute(async (req, res) => {
|
|
|
231
269
|
user: userAfterLinkSent,
|
|
232
270
|
authToken,
|
|
233
271
|
loginSource: LOGIN_SOURCE,
|
|
234
|
-
additionalEventData:
|
|
272
|
+
additionalEventData: authEventData,
|
|
235
273
|
});
|
|
236
274
|
|
|
275
|
+
// Union of both hooks' auto-signups, deduped by product so a tenant that
|
|
276
|
+
// opts into the same newsletter from both hooks yields one subscribe event,
|
|
277
|
+
// not two.
|
|
278
|
+
const seenProducts = new Set();
|
|
279
|
+
const autoSignups = [
|
|
280
|
+
...asArray(linkSentEventData.autoSignups),
|
|
281
|
+
...asArray(authEventData.autoSignups),
|
|
282
|
+
]
|
|
283
|
+
.filter((signup) => {
|
|
284
|
+
const key = signup.productId != null
|
|
285
|
+
? `product:${signup.productId}`
|
|
286
|
+
: `entity:${signup.subscriptionEntity}`;
|
|
287
|
+
if (seenProducts.has(key)) return false;
|
|
288
|
+
seenProducts.add(key);
|
|
289
|
+
return true;
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
const additionalEventData = {
|
|
293
|
+
...linkSentEventData,
|
|
294
|
+
...authEventData,
|
|
295
|
+
...(autoSignups.length && { autoSignups }),
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
// A new user with zero auto-signups is the shape this whole bug had: the
|
|
299
|
+
// sign-in succeeds, Omeda is updated, and GA4 records no conversion. Surface
|
|
300
|
+
// the count so a regression is a visible number on the transaction rather
|
|
301
|
+
// than something a stakeholder notices in a report weeks later.
|
|
302
|
+
addAttrs({ idxAutoSignupCount: autoSignups.length });
|
|
303
|
+
|
|
237
304
|
setStep('complete');
|
|
238
305
|
res.json({
|
|
239
306
|
ok: true,
|
|
@@ -242,6 +309,7 @@ module.exports = asyncRoute(async (req, res) => {
|
|
|
242
309
|
applicationId: config.getAppId(),
|
|
243
310
|
user: userAfterLinkSent,
|
|
244
311
|
loginSource: LOGIN_SOURCE,
|
|
312
|
+
additionalEventData,
|
|
245
313
|
entity,
|
|
246
314
|
});
|
|
247
315
|
});
|