Skip to content

Commit adf3a75

Browse files
committed
fix(sdk-install): identity-verification truths from a live install run (0.3.1)
A real from-zero install against an enforcing environment corrected three claims and added the trap that actually bit: - "silently rejected" was imprecise: the identify() promise REJECTS with a misleading generic "check your network connection" message — identical for missing token / bad signature / no active secret — and a fire-and-forget call surfaces only as an empty-reason Uncaught (in promise). SKILL.md, identity-verification.md and troubleshooting.md now describe that real signal (and advise awaiting identify during rollout). - New headline trap: even CORRECTLY signed tokens fail when the target environment has no ACTIVE secret — classically a secret copied from another environment's settings page. Secrets are per-environment. - Secret lifecycle is console-only (the API/MCP deliberately has no signing-secret surface) — confirm registration with the admin before testing enforcement. - verify.md: driving the widget programmatically — a11y snapshots show the iframe as an opaque frame (dispatch via contentDocument), and some controls need the full pointer event sequence, not a bare .click(). Version 0.3.0 → 0.3.1 across the six manifests (validate.js green).
1 parent 5c1decf commit adf3a75

10 files changed

Lines changed: 57 additions & 21 deletions

File tree

.agents/plugins/marketplace.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"plugins": [
33
{
44
"name": "usertour",
5-
"version": "0.3.0",
5+
"version": "0.3.1",
66
"category": "Coding",
77
"source": {
88
"type": "local",

.claude-plugin/marketplace.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"name": "usertour",
1111
"source": "./",
1212
"description": "Skills for authoring Usertour in-app onboarding content",
13-
"version": "0.3.0"
13+
"version": "0.3.1"
1414
}
1515
]
1616
}

.claude-plugin/plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "usertour",
33
"description": "Skills for Usertour in-app onboarding — authoring content (flows, checklists, launchers, banners, surveys) and installing the Web SDK — via the Usertour API/MCP",
4-
"version": "0.3.0",
4+
"version": "0.3.1",
55
"author": {
66
"name": "Usertour",
77
"url": "https://www.usertour.io"

.codex-plugin/plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "usertour",
3-
"version": "0.3.0",
3+
"version": "0.3.1",
44
"description": "Skills for Usertour in-app onboarding — authoring content (flows, checklists, launchers, banners, surveys) and installing the Web SDK — via the Usertour API/MCP",
55
"author": {
66
"name": "Usertour",

.cursor-plugin/marketplace.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"name": "usertour",
1010
"source": "./",
1111
"description": "Skills for authoring Usertour in-app onboarding content",
12-
"version": "0.3.0"
12+
"version": "0.3.1"
1313
}
1414
]
1515
}

.cursor-plugin/plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "usertour",
3-
"version": "0.3.0",
3+
"version": "0.3.1",
44
"description": "Skills for Usertour in-app onboarding — authoring content (flows, checklists, launchers, banners, surveys) and installing the Web SDK — via the Usertour API/MCP",
55
"author": {
66
"name": "Usertour",

skills/usertour-sdk-install/SKILL.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ ask the user for the environment token (Settings → Environments) — it is the
5656
target, or published content silently never shows. Call `usertour.reset()` on
5757
logout. If the environment **requires identity verification**, `identify()` /
5858
`group()` must also carry a backend-signed JWT (the `{ token }` option) or the
59-
identity is **silently rejected** at connect — see
59+
identity is rejected at connect — near-silently: the only signal is the
60+
`identify()` promise rejecting with a misleading network-ish message — see
6061
[references/identity-verification.md](references/identity-verification.md).
6162
6. **SPA routing** — for single-page apps, confirm content re-evaluates on route
6263
change (see the framework reference).
@@ -74,8 +75,9 @@ ask the user for the environment token (Settings → Environments) — it is the
7475
segments / start-rules, or nothing renders (the #1 "it's not showing" cause).
7576
- **Enforced identity verification:** when the target environment requires it,
7677
the install is not done until `identify()`/`group()` carry a valid signed
77-
token — unsigned calls are rejected **silently** (looks exactly like
78-
"identify ignored"). See
78+
token — unsigned calls are rejected near-silently (the promise rejects with a
79+
misleading "check your network" message; looks exactly like "identify
80+
ignored"). See
7981
[references/identity-verification.md](references/identity-verification.md).
8082
- **Defer the API to the docs:** this skill is the wiring + gotchas, not a copy of
8183
the snippet. Read the reference; it's the source of truth.

skills/usertour-sdk-install/references/identity-verification.md

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,15 @@
22

33
An environment can **require identity verification**: every `identify()` /
44
`group()` call must carry a JWT minted by the app's backend, or the server
5-
rejects the claimed identity **at connect time — silently**. No SDK error, no
6-
console noise; the app looks installed but the user never appears in Usertour
7-
and nothing renders. If the target environment has enforcement ON, the install
8-
is NOT done until this is wired.
5+
rejects the claimed identity at connect time. The rejection is NEARLY silent —
6+
the user never appears in Usertour and nothing renders — and the one signal it
7+
does emit is **misleading**: the `identify()` promise REJECTS with a generic
8+
"request unsuccessful … check your network connection" message (never mentioning
9+
identity verification), and a fire-and-forget `identify()` surfaces only as an
10+
`Uncaught (in promise)` with an empty reason. The message is IDENTICAL for a
11+
missing token, a bad signature, and a no-active-secret environment — you cannot
12+
tell them apart client-side. If the target environment has enforcement ON, the
13+
install is NOT done until this is wired.
914

1015
Live guide (prefer it over this summary):
1116
https://docs.usertour.io/developers/identity-verification
@@ -17,6 +22,11 @@ https://docs.usertour.io/developers/identity-verification
1722
a "signed traffic" coverage stat.
1823
- Rule of thumb: wire it BEFORE enforcement is enabled — the intended rollout is
1924
"sign all traffic first, watch coverage hit 100%, then enforce".
25+
- **The secret lifecycle is console-only**: the API/MCP has no signing-secret
26+
surface (deliberate — a secret grants identity forgery), so you cannot create,
27+
list, or check secrets from here. Before testing against enforcement, have the
28+
admin confirm a secret is registered **in the environment you're targeting**
29+
and hand it to your backend config.
2030

2131
## Backend: mint the token
2232

@@ -70,7 +80,16 @@ usertour.group(company.id, companyAttributes, { token: usertourToken });
7080
wrong algorithm (must be HS256) / malformed / missing `sub` / no active secret.
7181
- The **coverage stat** (last 7 days: valid / invalid / unsigned) tells you
7282
whether real traffic is signed before you flip enforcement.
83+
- **Even CORRECTLY signed tokens fail?** First check the environment has an
84+
ACTIVE secret at all: with none registered, the server rejects every
85+
non-anonymous identity (`no_active_secret`) and it looks exactly like a bad
86+
signature. The classic cause is a secret copied from ANOTHER environment's
87+
settings page — secrets are per-environment; a Production secret proves
88+
nothing in Development. (Verified the hard way in a real install run.)
7389
- Symptom map: user identifies fine in an environment WITHOUT enforcement but
74-
vanishes in the enforced one → the token is missing/invalid there (different
75-
environment = different signing secret; check which secret the backend uses
76-
per environment).
90+
vanishes in the enforced one → the token is missing/invalid there, or that
91+
environment has no active secret (different environment = different signing
92+
secret; check which secret the backend uses per environment).
93+
- Client-side, the only tell of ANY rejection is the `identify()` promise
94+
rejecting with the generic network-ish message above — `await`/`.catch()` it
95+
during rollout so rejections are at least visible.

skills/usertour-sdk-install/references/troubleshooting.md

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,23 @@ Non-negotiables. Check the linkage before anything else.
1616
## `identify()` looks ignored — the user never appears in Usertour at all
1717

1818
**Cause:** The environment **enforces identity verification** and the
19-
`identify()` / `group()` call carried no (or an invalid) backend-signed JWT. The
20-
server rejects the identity **at connect time, silently** — no SDK error, the
21-
user is never created, nothing renders. A tell: the same code works in an
22-
environment without enforcement but "does nothing" in the enforced one.
19+
`identify()` / `group()` call carried no (or an invalid) backend-signed JWT — or
20+
the environment has NO active signing secret at all, which rejects even
21+
correctly-signed tokens. The server rejects the identity at connect time; the
22+
user is never created, nothing renders. The only client-side signal is the
23+
`identify()` promise rejecting with a MISLEADING generic message ("…check your
24+
network connection…" — identical for missing token / bad signature / no active
25+
secret); fire-and-forget calls show it only as an `Uncaught (in promise)` with
26+
an empty reason. A tell: the same code works in an environment without
27+
enforcement but "does nothing" in the enforced one.
2328
**Solution:** Wire the signed token
2429
([identity-verification.md](identity-verification.md)); validate a
2530
backend-minted JWT in Settings → Identity Verification (it names the exact
2631
problem: expired / wrong algorithm — HS256 only / missing `sub` / signed with a
27-
revoked secret / …). Remember each environment has its OWN signing secret.
32+
revoked secret / no active secret / …). Remember each environment has its OWN
33+
signing secret — a secret copied from another environment fails exactly like a
34+
wrong one, and the secret lifecycle is console-only (the API/MCP can't create
35+
or list secrets).
2836

2937
## A flow does a full page reload mid-tour (SPA)
3038

skills/usertour-sdk-install/references/verify.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,13 @@ one wired):
3535
The SDK swaps the iframe surface per step, so re-query it each step — a
3636
`contentDocument` reference held from a previous step goes stale (reads null),
3737
and a stale null can look like a trigger that didn't fire.
38+
- **Driving (not just reading) the widget programmatically** has two extra
39+
gotchas: accessibility snapshots show the iframe as an opaque "Content Frame"
40+
(no clickable uids inside — dispatch events via `contentDocument` instead),
41+
and some controls (e.g. the resource-center launcher) ignore a bare
42+
`.click()` — dispatch the full pointer sequence
43+
(`pointerdown → mousedown → pointerup → mouseup → click`) when a programmatic
44+
click seems to do nothing.
3845

3946
## 4. Security check
4047

0 commit comments

Comments
 (0)