@growth-labs/auth 0.7.4 → 0.8.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.
@@ -1 +1 @@
1
- {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../src/routes/api/shared.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;AAEjD,OAAO,EAAE,KAAK,oBAAoB,EAAE,KAAK,kBAAkB,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAChG,OAAO,EACN,KAAK,qBAAqB,EAI1B,MAAM,0BAA0B,CAAA;AAWjC,OAAO,EAAE,KAAK,YAAY,EAAE,QAAQ,EAAoB,UAAU,EAAE,MAAM,YAAY,CAAA;AAItF,wBAAsB,cAAc,CACnC,IAAI,EAAE,QAAQ,EACd,GAAG,EAAE,MAAM,EACX,OAAO,GAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAO,GAC9B,OAAO,CAAC,MAAM,CAAC,CAGjB;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,YAAY,GAAG,QAAQ,GAAG,IAAI,CAe1E;AAED,wBAAgB,gBAAgB,CAC/B,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,oBAAoB,EAC5B,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EAC/C,QAAQ,CAAC,EAAE,UAAU,GAAG,YAAY,GAClC,OAAO,CAAC,QAAQ,CAAC,GAAG,QAAQ,CAsC9B;AAED,wBAAgB,cAAc,CAC7B,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,kBAAkB,EAC1B,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EACjD,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EAC/C,QAAQ,CAAC,EAAE,qBAAqB,GAC9B,QAAQ,CAMV;AAyBD,YAAY,EAAE,YAAY,EAAE,CAAA;AAC5B,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAA"}
1
+ {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../src/routes/api/shared.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;AAEjD,OAAO,EAAE,KAAK,oBAAoB,EAAE,KAAK,kBAAkB,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAChG,OAAO,EACN,KAAK,qBAAqB,EAI1B,MAAM,0BAA0B,CAAA;AAYjC,OAAO,EAAE,KAAK,YAAY,EAAE,QAAQ,EAAoB,UAAU,EAAE,MAAM,YAAY,CAAA;AAItF,wBAAsB,cAAc,CACnC,IAAI,EAAE,QAAQ,EACd,GAAG,EAAE,MAAM,EACX,OAAO,GAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAO,GAC9B,OAAO,CAAC,MAAM,CAAC,CAGjB;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,YAAY,GAAG,QAAQ,GAAG,IAAI,CAe1E;AAED,wBAAgB,gBAAgB,CAC/B,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,oBAAoB,EAC5B,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EAC/C,QAAQ,CAAC,EAAE,UAAU,GAAG,YAAY,GAClC,OAAO,CAAC,QAAQ,CAAC,GAAG,QAAQ,CAsC9B;AAED,wBAAgB,cAAc,CAC7B,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,kBAAkB,EAC1B,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EACjD,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EAC/C,QAAQ,CAAC,EAAE,qBAAqB,GAC9B,QAAQ,CAMV;AAyBD,YAAY,EAAE,YAAY,EAAE,CAAA;AAC5B,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAA"}
@@ -3,6 +3,7 @@ import { getRuntimeEnv } from '../../runtime.js';
3
3
  import { authClient } from '../../server.js';
4
4
  import { analyticsAvailable, emitAuthFailureEvent, emitAuthSuccessEvent, } from '../../utils/analytics.js';
5
5
  import { clearAuthFlowCookies, getCookie, setCookie, setIdentityCookie, setTokenCookies, } from '../../utils/cookies.js';
6
+ import { canonicalIdentitySubject } from '../../utils/identity.js';
6
7
  import { createIdentityCookiePayload, signIdentityCookie } from '../../utils/identity-cookie.js';
7
8
  import { exchangeCode } from '../../utils/openauth.js';
8
9
  import { resolveWaitUntil } from '../../utils/wait-until.js';
@@ -98,13 +99,17 @@ async function completeJsonAuth(context, code, errorPath, errorParams, provider)
98
99
  const response = mutableHeaders(Response.json({ ok: true, redirect }));
99
100
  setTokenCookies(response, options, tokenResponse.accessToken, tokenResponse.refreshToken);
100
101
  clearAuthFlowCookies(response, options);
101
- await setOptionalIdentityCookie(response, tokenResponse.claims.sub);
102
+ // Identity-join contract §3/§5: hash the byte-identical canonical subject
103
+ // for BOTH the cookie payload and the login-time analytics emit (see the
104
+ // GET callback for the full rationale). Coerce once, use for both.
105
+ const identitySubject = canonicalIdentitySubject(tokenResponse.claims.sub);
106
+ await setOptionalIdentityCookie(response, identitySubject);
102
107
  if (analyticsAvailable(context)) {
103
108
  resolveWaitUntil(context)(emitAuthSuccessEvent(context, {
104
109
  provider: tokenResponse.provider,
105
110
  isNewUser: tokenResponse.isNewUser,
106
111
  authPath: context.url.pathname,
107
- identityUserId: tokenResponse.claims.sub,
112
+ identityUserId: identitySubject,
108
113
  }));
109
114
  }
110
115
  return response;
@@ -1 +1 @@
1
- {"version":3,"file":"shared.js","sourceRoot":"","sources":["../../../src/routes/api/shared.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"shared.js","sourceRoot":"","sources":["../../../src/routes/api/shared.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"callback.d.ts","sourceRoot":"","sources":["../../src/routes/callback.ts"],"names":[],"mappings":"AAkBA,UAAU,eAAe;IACxB,OAAO,EAAE,OAAO,CAAA;IAChB,GAAG,EAAE,GAAG,CAAA;IACR,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAKhC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK,IAAI,CAAA;CAC/C;AAoCD,wBAAsB,GAAG,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,QAAQ,CAAC,CA0GrE"}
1
+ {"version":3,"file":"callback.d.ts","sourceRoot":"","sources":["../../src/routes/callback.ts"],"names":[],"mappings":"AAmBA,UAAU,eAAe;IACxB,OAAO,EAAE,OAAO,CAAA;IAChB,GAAG,EAAE,GAAG,CAAA;IACR,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAKhC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK,IAAI,CAAA;CAC/C;AAoCD,wBAAsB,GAAG,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,QAAQ,CAAC,CAsHrE"}
@@ -2,6 +2,7 @@ import { getConfig } from '../config-store.js';
2
2
  import { getRuntimeEnv } from '../runtime.js';
3
3
  import { analyticsAvailable, emitAuthFailureEvent, emitAuthSuccessEvent, } from '../utils/analytics.js';
4
4
  import { clearAuthFlowCookies, getCookie, setIdentityCookie, setTokenCookies, } from '../utils/cookies.js';
5
+ import { canonicalIdentitySubject } from '../utils/identity.js';
5
6
  import { createIdentityCookiePayload, signIdentityCookie } from '../utils/identity-cookie.js';
6
7
  import { exchangeCode } from '../utils/openauth.js';
7
8
  import { verifyState } from '../utils/state.js';
@@ -83,10 +84,16 @@ export async function GET(context) {
83
84
  });
84
85
  setTokenCookies(mutableResponse, options, tokenResponse.accessToken, tokenResponse.refreshToken);
85
86
  clearAuthFlowCookies(mutableResponse, options);
87
+ // Identity-join contract §3/§5: the cookie hash and the analytics
88
+ // re-derivation MUST hash the byte-identical subject string. Coerce the
89
+ // issuer `sub` to the canonical prefixed `user:<uuid>` form ONCE and use it
90
+ // for BOTH the cookie payload and the login-time analytics emit, so they
91
+ // can never diverge (and a bare-uuid subject is prefixed before hashing).
92
+ const identitySubject = canonicalIdentitySubject(tokenResponse.claims.sub);
86
93
  const identitySecret = getIdentitySecret();
87
94
  if (identitySecret) {
88
95
  try {
89
- setIdentityCookie(mutableResponse, await signIdentityCookie(await createIdentityCookiePayload(tokenResponse.claims.sub, identitySecret), identitySecret));
96
+ setIdentityCookie(mutableResponse, await signIdentityCookie(await createIdentityCookiePayload(identitySubject, identitySecret), identitySecret));
90
97
  }
91
98
  catch {
92
99
  // Keep login working if the optional analytics identity secret is misconfigured.
@@ -99,12 +106,18 @@ export async function GET(context) {
99
106
  // terminates the orphaned promise before the conversion event lands —
100
107
  // the root cause of the missing `login_completed` / `signup_completed`
101
108
  // rows in `gl_conversion_events` after the Fronts cutover.
109
+ //
110
+ // Identity-join contract §4 (OQ-3): the emit carries the canonical
111
+ // prefixed subject as `identity_user_id`. `@growth-labs/analytics`
112
+ // re-derives the same hash and stamps `gl_identity_links.identity_user_id`
113
+ // with the raw `<uuid>` (prefix stripped analytics-side) — the strongest
114
+ // forward guarantee that the translation key is stamped from first login.
102
115
  if (analyticsAvailable(context)) {
103
116
  const promise = emitAuthSuccessEvent(context, {
104
117
  provider: tokenResponse.provider,
105
118
  isNewUser: tokenResponse.isNewUser,
106
119
  authPath,
107
- identityUserId: tokenResponse.claims.sub,
120
+ identityUserId: identitySubject,
108
121
  });
109
122
  resolveWaitUntil(context)(promise);
110
123
  }
@@ -1 +1 @@
1
- {"version":3,"file":"callback.js","sourceRoot":"","sources":["../../src/routes/callback.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"callback.js","sourceRoot":"","sources":["../../src/routes/callback.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"analytics.d.ts","sourceRoot":"","sources":["../../src/utils/analytics.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,qBAAqB,GAAG,QAAQ,GAAG,YAAY,GAAG,UAAU,GAAG,SAAS,CAAA;AAEpF,MAAM,WAAW,qBAAqB;IACrC,QAAQ,EAAE,qBAAqB,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;IAC3D,SAAS,EAAE,OAAO,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,cAAc,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,qBAAqB;IACrC,QAAQ,EAAE,qBAAqB,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;IAC3D,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;IACjC,QAAQ,EAAE,MAAM,CAAA;CAChB;AAiDD,wBAAsB,aAAa,CAClC,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC3B,OAAO,CAAC,IAAI,CAAC,CAIf;AAED,wBAAgB,qBAAqB,CACpC,QAAQ,EAAE,qBAAqB,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,GACzD,qBAAqB,CAEvB;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAGpF;AAED,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,OAAO,GAAG,kBAAkB,GAAG,iBAAiB,CAE/F;AAED,wBAAsB,oBAAoB,CACzC,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,qBAAqB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAOf;AAED,wBAAsB,oBAAoB,CACzC,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,qBAAqB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAMf;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GAAG,OAAO,CAGzF;AAED,wBAAsB,cAAc,CACnC,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,MAAM,EACjB,IAAI,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAChC,OAAO,CAAC,IAAI,CAAC,CAEf;AAED,wBAAgB,eAAe,IAAI,IAAI,CAItC"}
1
+ {"version":3,"file":"analytics.d.ts","sourceRoot":"","sources":["../../src/utils/analytics.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,qBAAqB,GAAG,QAAQ,GAAG,YAAY,GAAG,UAAU,GAAG,SAAS,CAAA;AAEpF,MAAM,WAAW,qBAAqB;IACrC,QAAQ,EAAE,qBAAqB,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;IAC3D,SAAS,EAAE,OAAO,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,cAAc,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,qBAAqB;IACrC,QAAQ,EAAE,qBAAqB,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;IAC3D,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;IACjC,QAAQ,EAAE,MAAM,CAAA;CAChB;AAiBD,wBAAsB,aAAa,CAClC,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC3B,OAAO,CAAC,IAAI,CAAC,CASf;AAED,wBAAgB,qBAAqB,CACpC,QAAQ,EAAE,qBAAqB,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,GACzD,qBAAqB,CAEvB;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAGpF;AAED,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,OAAO,GAAG,kBAAkB,GAAG,iBAAiB,CAE/F;AAED,wBAAsB,oBAAoB,CACzC,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,qBAAqB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAOf;AAED,wBAAsB,oBAAoB,CACzC,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,qBAAqB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAMf;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GAAG,OAAO,CAGzF;AAED,wBAAsB,cAAc,CACnC,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,MAAM,EACjB,IAAI,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAChC,OAAO,CAAC,IAAI,CAAC,CAEf;AAKD,wBAAgB,eAAe,IAAI,IAAI,CAEtC"}
@@ -1,7 +1,4 @@
1
- let trackFn = null;
2
- let resolved = false;
3
- let resolvePromise = null;
4
- const ANALYTICS_UTILS_MODULE = '@growth-labs/analytics/utils';
1
+ import { trackServerEvent } from '@growth-labs/analytics/utils';
5
2
  const AUTH_PROVIDERS = new Set(['google', 'email-code', 'password']);
6
3
  const AUTH_FAILURE_REASONS = new Set([
7
4
  'auth_failed',
@@ -16,40 +13,15 @@ const AUTH_FAILURE_REASONS = new Set([
16
13
  'send_failed',
17
14
  'token_exchange_failed',
18
15
  ]);
19
- async function importOptionalModule(modulePath) {
20
- try {
21
- return (await import(/* @vite-ignore */ modulePath));
22
- }
23
- catch {
24
- return null;
25
- }
26
- }
27
- async function resolveAnalytics() {
28
- if (resolved)
29
- return;
30
- if (!resolvePromise) {
31
- resolvePromise = (async () => {
32
- const mod = await importOptionalModule(ANALYTICS_UTILS_MODULE);
33
- let candidate;
34
- try {
35
- candidate = mod?.trackServerEvent;
36
- }
37
- catch {
38
- candidate = undefined;
39
- }
40
- if (typeof candidate === 'function') {
41
- trackFn = candidate;
42
- }
43
- resolved = true;
44
- })();
45
- }
46
- await resolvePromise;
47
- }
48
16
  export async function emitAuthEvent(context, eventName, data) {
49
- await resolveAnalytics();
50
- if (!trackFn)
51
- return;
52
- return trackFn(context, eventName, data);
17
+ // Bind via static import so wrangler/esbuild bundles the target at build
18
+ // time. The previous variable-path dynamic import (await import(modulePath))
19
+ // could not be statically analyzed; in Cloudflare Workers the bundle did
20
+ // not include @growth-labs/analytics/utils, the runtime resolved null, and
21
+ // every auth event silently no-op'd — surfaced post-2026-05-22 as
22
+ // `signup_completed` / `login_completed` rows never landing in
23
+ // gl_conversion_events on the new Fronts Worker. See peerDependency.
24
+ await trackServerEvent(context, eventName, data);
53
25
  }
54
26
  export function normalizeAuthProvider(provider) {
55
27
  return provider && AUTH_PROVIDERS.has(provider) ? provider : 'unknown';
@@ -84,9 +56,10 @@ export function analyticsAvailable(context) {
84
56
  export async function trackAuthEvent(context, eventName, data = {}) {
85
57
  return emitAuthEvent(context, eventName, data);
86
58
  }
59
+ // Test-only export retained for compatibility with existing tests that reset
60
+ // internal state between runs. Now a no-op since there's no internal state
61
+ // to reset — trackFn is the bound static import.
87
62
  export function _resetAnalytics() {
88
- trackFn = null;
89
- resolved = false;
90
- resolvePromise = null;
63
+ // noop
91
64
  }
92
65
  //# sourceMappingURL=analytics.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"analytics.js","sourceRoot":"","sources":["../../src/utils/analytics.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"analytics.js","sourceRoot":"","sources":["../../src/utils/analytics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAA;AAiB/D,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,YAAY,EAAE,UAAU,CAAC,CAAC,CAAA;AACpE,MAAM,oBAAoB,GAAG,IAAI,GAAG,CAAC;IACpC,aAAa;IACb,qBAAqB;IACrB,cAAc;IACd,iBAAiB;IACjB,kBAAkB;IAClB,eAAe;IACf,mBAAmB;IACnB,cAAc;IACd,gBAAgB;IAChB,aAAa;IACb,uBAAuB;CACvB,CAAC,CAAA;AAEF,MAAM,CAAC,KAAK,UAAU,aAAa,CAClC,OAAgB,EAChB,SAAiB,EACjB,IAA6B;IAE7B,yEAAyE;IACzE,6EAA6E;IAC7E,yEAAyE;IACzE,2EAA2E;IAC3E,kEAAkE;IAClE,+DAA+D;IAC/D,qEAAqE;IACrE,MAAM,gBAAgB,CAAC,OAAiD,EAAE,SAAS,EAAE,IAAI,CAAC,CAAA;AAC3F,CAAC;AAED,MAAM,UAAU,qBAAqB,CACpC,QAA2D;IAE3D,OAAO,QAAQ,IAAI,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAE,QAAkC,CAAC,CAAC,CAAC,SAAS,CAAA;AAClG,CAAC;AAED,MAAM,UAAU,0BAA0B,CAAC,MAAiC;IAC3E,IAAI,MAAM,KAAK,eAAe;QAAE,OAAO,cAAc,CAAA;IACrD,OAAO,MAAM,IAAI,oBAAoB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,aAAa,CAAA;AAC3E,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,SAAkB;IACtD,OAAO,SAAS,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,iBAAiB,CAAA;AAC1D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACzC,OAAgB,EAChB,KAA4B;IAE5B,OAAO,aAAa,CAAC,OAAO,EAAE,oBAAoB,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE;QACpE,QAAQ,EAAE,qBAAqB,CAAC,KAAK,CAAC,QAAQ,CAAC;QAC/C,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,SAAS,EAAE,KAAK,CAAC,QAAQ;QACzB,gBAAgB,EAAE,KAAK,CAAC,cAAc;KACtC,CAAC,CAAA;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACzC,OAAgB,EAChB,KAA4B;IAE5B,OAAO,aAAa,CAAC,OAAO,EAAE,aAAa,EAAE;QAC5C,QAAQ,EAAE,qBAAqB,CAAC,KAAK,CAAC,QAAQ,CAAC;QAC/C,MAAM,EAAE,0BAA0B,CAAC,KAAK,CAAC,MAAM,CAAC;QAChD,SAAS,EAAE,KAAK,CAAC,QAAQ;KACzB,CAAC,CAAA;AACH,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,OAA6C;IAC/E,KAAK,OAAO,CAAA;IACZ,OAAO,IAAI,CAAA;AACZ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CACnC,OAAgB,EAChB,SAAiB,EACjB,OAAgC,EAAE;IAElC,OAAO,aAAa,CAAC,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,CAAA;AAC/C,CAAC;AAED,6EAA6E;AAC7E,2EAA2E;AAC3E,iDAAiD;AACjD,MAAM,UAAU,eAAe;IAC9B,OAAO;AACR,CAAC"}
@@ -12,24 +12,54 @@ export declare class IdentitySubFormatDriftError extends Error {
12
12
  constructor(message: string, observedSubject: string);
13
13
  }
14
14
  /**
15
- * Validate and unwrap a `sub` claim. Returns the bare ULID portion
16
- * (the value Fronts stores in `users.identity_user_id` post-migration
17
- * is the prefixed form; the bare ULID is the convenient handle for
18
- * matching against business-side records that pre-date the prefix).
15
+ * Validate and unwrap a `sub` claim. Returns the raw id portion (the
16
+ * value after the `user:` prefix — a 36-char UUIDv4 in prod, or a
17
+ * 26-char ULID from legacy issuers). The value Fronts stores in
18
+ * `users.identity_user_id` post-migration is the prefixed form; the raw
19
+ * id is the canonical person key per the identity-join contract.
19
20
  *
20
21
  * Throws `IdentitySubFormatDriftError` when the subject does not match
21
- * `user:<ULID>`. The error code `IDENTITY_SUB_FORMAT_DRIFT` is stable
22
- * for observability matching.
22
+ * `user:<uuid>` or the legacy `user:<ULID>`. The error code
23
+ * `IDENTITY_SUB_FORMAT_DRIFT` is stable for observability matching.
23
24
  */
24
25
  export declare function extractIdentityId(sub: string): string;
25
26
  /**
26
27
  * Convenience: normalize either form to the canonical prefixed `sub`.
27
- * Idempotent on already-prefixed input. Throws on invalid ULID.
28
+ * Idempotent on already-prefixed input. Accepts both a 36-char UUIDv4
29
+ * and a legacy 26-char ULID suffix; throws on a genuinely-malformed id.
28
30
  *
29
31
  * Useful inside a one-shot migration: pass each row's stored
30
32
  * `identity_user_id` through this and write back the result.
31
33
  */
32
34
  export declare function normalizeIdentitySubject(value: string): string;
35
+ /**
36
+ * Identity-join contract §3/§4 (OQ-3, login-time stamping): the auth
37
+ * callback holds both the subject and the hash when it mints the `gl_id`
38
+ * cookie, so it is the strongest place to stamp the analytics→user
39
+ * translation key — it emits a server analytics event carrying the
40
+ * subject, and `@growth-labs/analytics` re-derives the same hash and
41
+ * stamps `gl_identity_links.identity_user_id` with the raw `<uuid>`.
42
+ *
43
+ * For that round-trip to hold, the subject auth emits MUST be the exact
44
+ * **prefixed** `user:<uuid>` string the cookie hash is computed over
45
+ * (contract §3 pins `identity_id = SHA-256(subject + secret)` where
46
+ * `subject = "user:" + <uuid>`). `@growth-labs/analytics`'s
47
+ * `deriveOpaqueIdentityId` hashes the value verbatim and then strips a
48
+ * single leading `user:` to store the raw uuid in the column — so the
49
+ * canonical prefixed form is what must travel on the wire.
50
+ *
51
+ * This helper coerces the issuer's `sub` to that canonical prefixed
52
+ * form via `normalizeIdentitySubject` (UUIDv4-aware after #127). It is
53
+ * deliberately **non-throwing**: a genuinely-drifted subject must never
54
+ * break login, so on a validation failure it returns the original
55
+ * subject unchanged. The cookie producer (`createIdentityCookiePayload`)
56
+ * and this emit hash the *same* string in every case, so the
57
+ * events↔links join stays consistent even on the fallback path; the
58
+ * normalization only adds the forward guarantee that a bare-uuid subject
59
+ * is prefixed before hashing so the analytics column gets the right raw
60
+ * value.
61
+ */
62
+ export declare function canonicalIdentitySubject(sub: string): string;
33
63
  export interface BridgeMigrationOptions {
34
64
  /** Table that holds the bridge column. Default: `users`. */
35
65
  table?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../../src/utils/identity.ts"],"names":[],"mappings":"AAwBA;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,UAAU,CAAA;AAS9C,8CAA8C;AAC9C,eAAO,MAAM,yBAAyB,8BAA8B,CAAA;AAEpE,qBAAa,2BAA4B,SAAQ,KAAK;IAIpD,QAAQ,CAAC,eAAe,EAAE,MAAM;IAHjC,QAAQ,CAAC,IAAI,+BAA4B;gBAExC,OAAO,EAAE,MAAM,EACN,eAAe,EAAE,MAAM;CAKjC;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAsBrD;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAqB9D;AAID,MAAM,WAAW,sBAAsB;IACtC,4DAA4D;IAC5D,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,0EAA0E;IAC1E,MAAM,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,wBAAwB;IACxC,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,CAAC,EAAE,SAAS,OAAO,EAAE,CAAA;CAC7B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gCAAgC,CAC/C,OAAO,GAAE,sBAA2B,GAClC,wBAAwB,CAO1B;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,mCAAmC,CAClD,OAAO,GAAE,sBAA2B,GAClC,wBAAwB,CAS1B;AAED;;;;;;;;GAQG;AACH,UAAU,MAAM;IACf,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG;QACrB,KAAK,CAAC,CAAC,GAAG,OAAO,KAAK,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAA;KACvC,CAAA;CACD;AAED,wBAAsB,0BAA0B,CAC/C,EAAE,EAAE,MAAM,EACV,OAAO,GAAE,sBAA2B,GAClC,OAAO,CAAC,MAAM,CAAC,CAIjB;AAED;;;;;;;;GAQG;AACH,UAAU,SAAS;IAClB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG;QACrB,GAAG,IAAI,OAAO,CAAC;YAAE,IAAI,CAAC,EAAE;gBAAE,OAAO,CAAC,EAAE,MAAM,CAAC;gBAAC,YAAY,CAAC,EAAE,MAAM,CAAA;aAAE,CAAA;SAAE,CAAC,CAAA;KACtE,CAAA;CACD;AAED,wBAAsB,6BAA6B,CAClD,EAAE,EAAE,SAAS,EACb,OAAO,GAAE,sBAA2B,GAClC,OAAO,CAAC,MAAM,CAAC,CAIjB"}
1
+ {"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../../src/utils/identity.ts"],"names":[],"mappings":"AA4BA;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,UAAU,CAAA;AA0B9C,8CAA8C;AAC9C,eAAO,MAAM,yBAAyB,8BAA8B,CAAA;AAEpE,qBAAa,2BAA4B,SAAQ,KAAK;IAIpD,QAAQ,CAAC,eAAe,EAAE,MAAM;IAHjC,QAAQ,CAAC,IAAI,+BAA4B;gBAExC,OAAO,EAAE,MAAM,EACN,eAAe,EAAE,MAAM;CAKjC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAuBrD;AAED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAqB9D;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAM5D;AAID,MAAM,WAAW,sBAAsB;IACtC,4DAA4D;IAC5D,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,0EAA0E;IAC1E,MAAM,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,wBAAwB;IACxC,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,CAAC,EAAE,SAAS,OAAO,EAAE,CAAA;CAC7B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gCAAgC,CAC/C,OAAO,GAAE,sBAA2B,GAClC,wBAAwB,CAO1B;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,mCAAmC,CAClD,OAAO,GAAE,sBAA2B,GAClC,wBAAwB,CAS1B;AAED;;;;;;;;GAQG;AACH,UAAU,MAAM;IACf,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG;QACrB,KAAK,CAAC,CAAC,GAAG,OAAO,KAAK,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAA;KACvC,CAAA;CACD;AAED,wBAAsB,0BAA0B,CAC/C,EAAE,EAAE,MAAM,EACV,OAAO,GAAE,sBAA2B,GAClC,OAAO,CAAC,MAAM,CAAC,CAIjB;AAED;;;;;;;;GAQG;AACH,UAAU,SAAS;IAClB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG;QACrB,GAAG,IAAI,OAAO,CAAC;YAAE,IAAI,CAAC,EAAE;gBAAE,OAAO,CAAC,EAAE,MAAM,CAAC;gBAAC,YAAY,CAAC,EAAE,MAAM,CAAA;aAAE,CAAA;SAAE,CAAC,CAAA;KACtE,CAAA;CACD;AAED,wBAAsB,6BAA6B,CAClD,EAAE,EAAE,SAAS,EACb,OAAO,GAAE,sBAA2B,GAClC,OAAO,CAAC,MAAM,CAAC,CAIjB"}
@@ -1,7 +1,11 @@
1
1
  // Identity-bridge helpers. The OpenAuth issuer this package targets emits
2
- // `sub` claims in the form `user:<ULID>` (e.g. `user:01KP8KA31Z3AGRCC578R9HXW0H`).
3
- // The unprefixed bare-ULID form was used in early issuer releases and is
4
- // no longer the contract.
2
+ // `sub` claims in the form `user:<id>`, where `<id>` is the issuer's userId.
3
+ // Prod issuers emit a 36-char UUIDv4 (e.g.
4
+ // `user:000cf60e-b252-4b3d-8f1a-2c9e7d4a6b10`); early issuer releases used a
5
+ // 26-char Crockford ULID (e.g. `user:01KP8KA31Z3AGRCC578R9HXW0H`). Both
6
+ // suffix shapes are accepted — see the identity-join contract (canonical
7
+ // person key `user:<uuid>`). The unprefixed bare form was used in even
8
+ // earlier releases and is no longer the contract on the JWT `sub`.
5
9
  //
6
10
  // Two scenarios this module addresses:
7
11
  //
@@ -28,11 +32,25 @@
28
32
  */
29
33
  export const IDENTITY_SUBJECT_PREFIX = 'user:';
30
34
  /**
31
- * Documented ULID shape: 26 chars of Crockford base32. The issuer
32
- * uses ULIDv1 and never falls back to a generic UUID — pin the
33
- * stricter pattern so drift is caught early.
35
+ * Documented ULID shape: 26 chars of Crockford base32. Used by early
36
+ * issuer releases.
34
37
  */
35
38
  const ULID_PATTERN = /^[0-9A-HJKMNP-TV-Z]{26}$/;
39
+ /**
40
+ * Prod issuer shape: a 36-char UUIDv4, lower- or upper-case hex
41
+ * (`8-4-4-4-12`, version nibble `4`, variant nibble `8|9|a|b`). This is
42
+ * the canonical person-key suffix per the identity-join contract
43
+ * (`user:<uuid>`).
44
+ */
45
+ const UUIDV4_PATTERN = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/;
46
+ /**
47
+ * The issuer's `sub` suffix is valid if it matches either the current
48
+ * prod UUIDv4 shape or the legacy 26-char ULID shape. Both are accepted
49
+ * for backward compatibility; genuinely-malformed suffixes still fail.
50
+ */
51
+ function isValidIdentitySuffix(id) {
52
+ return UUIDV4_PATTERN.test(id) || ULID_PATTERN.test(id);
53
+ }
36
54
  /** Stable error code for sub-format drift. */
37
55
  export const IDENTITY_SUB_FORMAT_DRIFT = 'IDENTITY_SUB_FORMAT_DRIFT';
38
56
  export class IdentitySubFormatDriftError extends Error {
@@ -45,14 +63,15 @@ export class IdentitySubFormatDriftError extends Error {
45
63
  }
46
64
  }
47
65
  /**
48
- * Validate and unwrap a `sub` claim. Returns the bare ULID portion
49
- * (the value Fronts stores in `users.identity_user_id` post-migration
50
- * is the prefixed form; the bare ULID is the convenient handle for
51
- * matching against business-side records that pre-date the prefix).
66
+ * Validate and unwrap a `sub` claim. Returns the raw id portion (the
67
+ * value after the `user:` prefix — a 36-char UUIDv4 in prod, or a
68
+ * 26-char ULID from legacy issuers). The value Fronts stores in
69
+ * `users.identity_user_id` post-migration is the prefixed form; the raw
70
+ * id is the canonical person key per the identity-join contract.
52
71
  *
53
72
  * Throws `IdentitySubFormatDriftError` when the subject does not match
54
- * `user:<ULID>`. The error code `IDENTITY_SUB_FORMAT_DRIFT` is stable
55
- * for observability matching.
73
+ * `user:<uuid>` or the legacy `user:<ULID>`. The error code
74
+ * `IDENTITY_SUB_FORMAT_DRIFT` is stable for observability matching.
56
75
  */
57
76
  export function extractIdentityId(sub) {
58
77
  if (typeof sub !== 'string' || sub.length === 0) {
@@ -60,20 +79,22 @@ export function extractIdentityId(sub) {
60
79
  }
61
80
  if (!sub.startsWith(IDENTITY_SUBJECT_PREFIX)) {
62
81
  throw new IdentitySubFormatDriftError(`JWT sub claim "${sub}" is missing the required "${IDENTITY_SUBJECT_PREFIX}" prefix. ` +
63
- `The issuer contract pins the format as "${IDENTITY_SUBJECT_PREFIX}<ULID>". ` +
82
+ `The issuer contract pins the format as "${IDENTITY_SUBJECT_PREFIX}<uuid>" ` +
83
+ '(legacy issuers used a 26-char ULID). ' +
64
84
  'If your issuer was migrated from an older release, see ' +
65
85
  'packages-docs/auth-identity-bridge.md for the bridge-row migration helper.', sub);
66
86
  }
67
87
  const id = sub.slice(IDENTITY_SUBJECT_PREFIX.length);
68
- if (!ULID_PATTERN.test(id)) {
88
+ if (!isValidIdentitySuffix(id)) {
69
89
  throw new IdentitySubFormatDriftError(`JWT sub claim "${sub}" has the "${IDENTITY_SUBJECT_PREFIX}" prefix but the suffix ` +
70
- 'is not a valid 26-char Crockford base32 ULID.', sub);
90
+ 'is not a valid 36-char UUIDv4 nor a legacy 26-char Crockford base32 ULID.', sub);
71
91
  }
72
92
  return id;
73
93
  }
74
94
  /**
75
95
  * Convenience: normalize either form to the canonical prefixed `sub`.
76
- * Idempotent on already-prefixed input. Throws on invalid ULID.
96
+ * Idempotent on already-prefixed input. Accepts both a 36-char UUIDv4
97
+ * and a legacy 26-char ULID suffix; throws on a genuinely-malformed id.
77
98
  *
78
99
  * Useful inside a one-shot migration: pass each row's stored
79
100
  * `identity_user_id` through this and write back the result.
@@ -84,16 +105,51 @@ export function normalizeIdentitySubject(value) {
84
105
  }
85
106
  if (value.startsWith(IDENTITY_SUBJECT_PREFIX)) {
86
107
  const id = value.slice(IDENTITY_SUBJECT_PREFIX.length);
87
- if (!ULID_PATTERN.test(id)) {
88
- throw new IdentitySubFormatDriftError(`value "${value}" has the "${IDENTITY_SUBJECT_PREFIX}" prefix but the suffix is not a valid ULID.`, value);
108
+ if (!isValidIdentitySuffix(id)) {
109
+ throw new IdentitySubFormatDriftError(`value "${value}" has the "${IDENTITY_SUBJECT_PREFIX}" prefix but the suffix is not a valid UUIDv4 or ULID.`, value);
89
110
  }
90
111
  return value;
91
112
  }
92
- if (!ULID_PATTERN.test(value)) {
93
- throw new IdentitySubFormatDriftError(`value "${value}" is neither a prefixed "${IDENTITY_SUBJECT_PREFIX}<ULID>" nor a valid bare ULID.`, value);
113
+ if (!isValidIdentitySuffix(value)) {
114
+ throw new IdentitySubFormatDriftError(`value "${value}" is neither a prefixed "${IDENTITY_SUBJECT_PREFIX}<uuid>" nor a valid bare UUIDv4 / ULID.`, value);
94
115
  }
95
116
  return `${IDENTITY_SUBJECT_PREFIX}${value}`;
96
117
  }
118
+ /**
119
+ * Identity-join contract §3/§4 (OQ-3, login-time stamping): the auth
120
+ * callback holds both the subject and the hash when it mints the `gl_id`
121
+ * cookie, so it is the strongest place to stamp the analytics→user
122
+ * translation key — it emits a server analytics event carrying the
123
+ * subject, and `@growth-labs/analytics` re-derives the same hash and
124
+ * stamps `gl_identity_links.identity_user_id` with the raw `<uuid>`.
125
+ *
126
+ * For that round-trip to hold, the subject auth emits MUST be the exact
127
+ * **prefixed** `user:<uuid>` string the cookie hash is computed over
128
+ * (contract §3 pins `identity_id = SHA-256(subject + secret)` where
129
+ * `subject = "user:" + <uuid>`). `@growth-labs/analytics`'s
130
+ * `deriveOpaqueIdentityId` hashes the value verbatim and then strips a
131
+ * single leading `user:` to store the raw uuid in the column — so the
132
+ * canonical prefixed form is what must travel on the wire.
133
+ *
134
+ * This helper coerces the issuer's `sub` to that canonical prefixed
135
+ * form via `normalizeIdentitySubject` (UUIDv4-aware after #127). It is
136
+ * deliberately **non-throwing**: a genuinely-drifted subject must never
137
+ * break login, so on a validation failure it returns the original
138
+ * subject unchanged. The cookie producer (`createIdentityCookiePayload`)
139
+ * and this emit hash the *same* string in every case, so the
140
+ * events↔links join stays consistent even on the fallback path; the
141
+ * normalization only adds the forward guarantee that a bare-uuid subject
142
+ * is prefixed before hashing so the analytics column gets the right raw
143
+ * value.
144
+ */
145
+ export function canonicalIdentitySubject(sub) {
146
+ try {
147
+ return normalizeIdentitySubject(sub);
148
+ }
149
+ catch {
150
+ return sub;
151
+ }
152
+ }
97
153
  /**
98
154
  * Build the read-only canary query consumers run before deploying the
99
155
  * issuer subject-format change. Returns the count of rows that hold
@@ -1 +1 @@
1
- {"version":3,"file":"identity.js","sourceRoot":"","sources":["../../src/utils/identity.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"identity.js","sourceRoot":"","sources":["../../src/utils/identity.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  export { type AuthAnalyticsProvider, type AuthFailureEventInput, type AuthSuccessEventInput, authSuccessEventName, emitAuthEvent, emitAuthFailureEvent, emitAuthSuccessEvent, normalizeAuthFailureReason, normalizeAuthProvider, trackAuthEvent, } from './analytics.js';
2
- export { type BridgeMigrationOptions, type BridgeMigrationStatement, buildLegacyIdentitySubjectCanary, buildLegacyIdentitySubjectMigration, extractIdentityId, findLegacyIdentitySubjects, IDENTITY_SUB_FORMAT_DRIFT, IDENTITY_SUBJECT_PREFIX, IdentitySubFormatDriftError, normalizeIdentitySubject, rewriteLegacyIdentitySubjects, } from './identity.js';
2
+ export { type BridgeMigrationOptions, type BridgeMigrationStatement, buildLegacyIdentitySubjectCanary, buildLegacyIdentitySubjectMigration, canonicalIdentitySubject, extractIdentityId, findLegacyIdentitySubjects, IDENTITY_SUB_FORMAT_DRIFT, IDENTITY_SUBJECT_PREFIX, IdentitySubFormatDriftError, normalizeIdentitySubject, rewriteLegacyIdentitySubjects, } from './identity.js';
3
3
  export { signIdentityCookie, verifyIdentityCookie } from './identity-cookie.js';
4
4
  export { getAuthorizationUrl, getMagicLinkUrl, getPasswordLoginUrl } from './openauth.js';
5
5
  export { createPKCEChallenge, generatePKCE } from './pkce.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAC1B,oBAAoB,EACpB,aAAa,EACb,oBAAoB,EACpB,oBAAoB,EACpB,0BAA0B,EAC1B,qBAAqB,EACrB,cAAc,GACd,MAAM,gBAAgB,CAAA;AACvB,OAAO,EACN,KAAK,sBAAsB,EAC3B,KAAK,wBAAwB,EAC7B,gCAAgC,EAChC,mCAAmC,EACnC,iBAAiB,EACjB,0BAA0B,EAC1B,yBAAyB,EACzB,uBAAuB,EACvB,2BAA2B,EAC3B,wBAAwB,EACxB,6BAA6B,GAC7B,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAA;AAC/E,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAA;AACzF,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAC7D,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AACvD,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAC1B,oBAAoB,EACpB,aAAa,EACb,oBAAoB,EACpB,oBAAoB,EACpB,0BAA0B,EAC1B,qBAAqB,EACrB,cAAc,GACd,MAAM,gBAAgB,CAAA;AACvB,OAAO,EACN,KAAK,sBAAsB,EAC3B,KAAK,wBAAwB,EAC7B,gCAAgC,EAChC,mCAAmC,EACnC,wBAAwB,EACxB,iBAAiB,EACjB,0BAA0B,EAC1B,yBAAyB,EACzB,uBAAuB,EACvB,2BAA2B,EAC3B,wBAAwB,EACxB,6BAA6B,GAC7B,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAA;AAC/E,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAA;AACzF,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAC7D,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AACvD,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA"}
@@ -1,5 +1,5 @@
1
1
  export { authSuccessEventName, emitAuthEvent, emitAuthFailureEvent, emitAuthSuccessEvent, normalizeAuthFailureReason, normalizeAuthProvider, trackAuthEvent, } from './analytics.js';
2
- export { buildLegacyIdentitySubjectCanary, buildLegacyIdentitySubjectMigration, extractIdentityId, findLegacyIdentitySubjects, IDENTITY_SUB_FORMAT_DRIFT, IDENTITY_SUBJECT_PREFIX, IdentitySubFormatDriftError, normalizeIdentitySubject, rewriteLegacyIdentitySubjects, } from './identity.js';
2
+ export { buildLegacyIdentitySubjectCanary, buildLegacyIdentitySubjectMigration, canonicalIdentitySubject, extractIdentityId, findLegacyIdentitySubjects, IDENTITY_SUB_FORMAT_DRIFT, IDENTITY_SUBJECT_PREFIX, IdentitySubFormatDriftError, normalizeIdentitySubject, rewriteLegacyIdentitySubjects, } from './identity.js';
3
3
  export { signIdentityCookie, verifyIdentityCookie } from './identity-cookie.js';
4
4
  export { getAuthorizationUrl, getMagicLinkUrl, getPasswordLoginUrl } from './openauth.js';
5
5
  export { createPKCEChallenge, generatePKCE } from './pkce.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAIN,oBAAoB,EACpB,aAAa,EACb,oBAAoB,EACpB,oBAAoB,EACpB,0BAA0B,EAC1B,qBAAqB,EACrB,cAAc,GACd,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAGN,gCAAgC,EAChC,mCAAmC,EACnC,iBAAiB,EACjB,0BAA0B,EAC1B,yBAAyB,EACzB,uBAAuB,EACvB,2BAA2B,EAC3B,wBAAwB,EACxB,6BAA6B,GAC7B,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAA;AAC/E,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAA;AACzF,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAC7D,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAIN,oBAAoB,EACpB,aAAa,EACb,oBAAoB,EACpB,oBAAoB,EACpB,0BAA0B,EAC1B,qBAAqB,EACrB,cAAc,GACd,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAGN,gCAAgC,EAChC,mCAAmC,EACnC,wBAAwB,EACxB,iBAAiB,EACjB,0BAA0B,EAC1B,yBAAyB,EACzB,uBAAuB,EACvB,2BAA2B,EAC3B,wBAAwB,EACxB,6BAA6B,GAC7B,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAA;AAC/E,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAA;AACzF,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAC7D,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@growth-labs/auth",
3
- "version": "0.7.4",
3
+ "version": "0.8.0",
4
4
  "type": "module",
5
5
  "types": "./dist/index.d.ts",
6
6
  "exports": {
@@ -46,12 +46,7 @@
46
46
  },
47
47
  "peerDependencies": {
48
48
  "astro": "^6.1.10",
49
- "@growth-labs/analytics": ">=0.4.0"
50
- },
51
- "peerDependenciesMeta": {
52
- "@growth-labs/analytics": {
53
- "optional": true
54
- }
49
+ "@growth-labs/analytics": ">=0.5.1"
55
50
  },
56
51
  "dependencies": {
57
52
  "zod": "^3.23.0",
@@ -62,7 +57,7 @@
62
57
  "typescript": "^5.8.0",
63
58
  "vite": "^7.3.2",
64
59
  "vitest": "^3.0.0",
65
- "@growth-labs/analytics": "0.5.1"
60
+ "@growth-labs/analytics": "0.8.0"
66
61
  },
67
62
  "scripts": {
68
63
  "build": "tsc",
@@ -14,6 +14,7 @@ import {
14
14
  setIdentityCookie,
15
15
  setTokenCookies,
16
16
  } from '../../utils/cookies.js'
17
+ import { canonicalIdentitySubject } from '../../utils/identity.js'
17
18
  import { createIdentityCookiePayload, signIdentityCookie } from '../../utils/identity-cookie.js'
18
19
  import { exchangeCode } from '../../utils/openauth.js'
19
20
  import { resolveWaitUntil } from '../../utils/wait-until.js'
@@ -174,14 +175,18 @@ async function completeJsonAuth(
174
175
 
175
176
  setTokenCookies(response, options, tokenResponse.accessToken, tokenResponse.refreshToken)
176
177
  clearAuthFlowCookies(response, options)
177
- await setOptionalIdentityCookie(response, tokenResponse.claims.sub)
178
+ // Identity-join contract §3/§5: hash the byte-identical canonical subject
179
+ // for BOTH the cookie payload and the login-time analytics emit (see the
180
+ // GET callback for the full rationale). Coerce once, use for both.
181
+ const identitySubject = canonicalIdentitySubject(tokenResponse.claims.sub)
182
+ await setOptionalIdentityCookie(response, identitySubject)
178
183
  if (analyticsAvailable(context)) {
179
184
  resolveWaitUntil(context)(
180
185
  emitAuthSuccessEvent(context, {
181
186
  provider: tokenResponse.provider,
182
187
  isNewUser: tokenResponse.isNewUser,
183
188
  authPath: context.url.pathname,
184
- identityUserId: tokenResponse.claims.sub,
189
+ identityUserId: identitySubject,
185
190
  }),
186
191
  )
187
192
  }
@@ -11,6 +11,7 @@ import {
11
11
  setIdentityCookie,
12
12
  setTokenCookies,
13
13
  } from '../utils/cookies.js'
14
+ import { canonicalIdentitySubject } from '../utils/identity.js'
14
15
  import { createIdentityCookiePayload, signIdentityCookie } from '../utils/identity-cookie.js'
15
16
  import { exchangeCode } from '../utils/openauth.js'
16
17
  import { verifyState } from '../utils/state.js'
@@ -134,13 +135,19 @@ export async function GET(context: CallbackContext): Promise<Response> {
134
135
 
135
136
  setTokenCookies(mutableResponse, options, tokenResponse.accessToken, tokenResponse.refreshToken)
136
137
  clearAuthFlowCookies(mutableResponse, options)
138
+ // Identity-join contract §3/§5: the cookie hash and the analytics
139
+ // re-derivation MUST hash the byte-identical subject string. Coerce the
140
+ // issuer `sub` to the canonical prefixed `user:<uuid>` form ONCE and use it
141
+ // for BOTH the cookie payload and the login-time analytics emit, so they
142
+ // can never diverge (and a bare-uuid subject is prefixed before hashing).
143
+ const identitySubject = canonicalIdentitySubject(tokenResponse.claims.sub)
137
144
  const identitySecret = getIdentitySecret()
138
145
  if (identitySecret) {
139
146
  try {
140
147
  setIdentityCookie(
141
148
  mutableResponse,
142
149
  await signIdentityCookie(
143
- await createIdentityCookiePayload(tokenResponse.claims.sub, identitySecret),
150
+ await createIdentityCookiePayload(identitySubject, identitySecret),
144
151
  identitySecret,
145
152
  ),
146
153
  )
@@ -156,12 +163,18 @@ export async function GET(context: CallbackContext): Promise<Response> {
156
163
  // terminates the orphaned promise before the conversion event lands —
157
164
  // the root cause of the missing `login_completed` / `signup_completed`
158
165
  // rows in `gl_conversion_events` after the Fronts cutover.
166
+ //
167
+ // Identity-join contract §4 (OQ-3): the emit carries the canonical
168
+ // prefixed subject as `identity_user_id`. `@growth-labs/analytics`
169
+ // re-derives the same hash and stamps `gl_identity_links.identity_user_id`
170
+ // with the raw `<uuid>` (prefix stripped analytics-side) — the strongest
171
+ // forward guarantee that the translation key is stamped from first login.
159
172
  if (analyticsAvailable(context)) {
160
173
  const promise = emitAuthSuccessEvent(context, {
161
174
  provider: tokenResponse.provider,
162
175
  isNewUser: tokenResponse.isNewUser,
163
176
  authPath,
164
- identityUserId: tokenResponse.claims.sub,
177
+ identityUserId: identitySubject,
165
178
  })
166
179
  resolveWaitUntil(context)(promise)
167
180
  }
@@ -1,4 +1,4 @@
1
- type TrackFn = (context: unknown, event: string, props?: Record<string, unknown>) => Promise<void>
1
+ import { trackServerEvent } from '@growth-labs/analytics/utils'
2
2
 
3
3
  export type AuthAnalyticsProvider = 'google' | 'email-code' | 'password' | 'unknown'
4
4
 
@@ -15,10 +15,6 @@ export interface AuthFailureEventInput {
15
15
  authPath: string
16
16
  }
17
17
 
18
- let trackFn: TrackFn | null = null
19
- let resolved = false
20
- let resolvePromise: Promise<void> | null = null
21
- const ANALYTICS_UTILS_MODULE = '@growth-labs/analytics/utils'
22
18
  const AUTH_PROVIDERS = new Set(['google', 'email-code', 'password'])
23
19
  const AUTH_FAILURE_REASONS = new Set([
24
20
  'auth_failed',
@@ -34,42 +30,19 @@ const AUTH_FAILURE_REASONS = new Set([
34
30
  'token_exchange_failed',
35
31
  ])
36
32
 
37
- async function importOptionalModule(modulePath: string): Promise<Record<string, unknown> | null> {
38
- try {
39
- return (await import(/* @vite-ignore */ modulePath)) as Record<string, unknown>
40
- } catch {
41
- return null
42
- }
43
- }
44
-
45
- async function resolveAnalytics(): Promise<void> {
46
- if (resolved) return
47
- if (!resolvePromise) {
48
- resolvePromise = (async () => {
49
- const mod = await importOptionalModule(ANALYTICS_UTILS_MODULE)
50
- let candidate: unknown
51
- try {
52
- candidate = mod?.trackServerEvent
53
- } catch {
54
- candidate = undefined
55
- }
56
- if (typeof candidate === 'function') {
57
- trackFn = candidate as TrackFn
58
- }
59
- resolved = true
60
- })()
61
- }
62
- await resolvePromise
63
- }
64
-
65
33
  export async function emitAuthEvent(
66
34
  context: unknown,
67
35
  eventName: string,
68
36
  data: Record<string, unknown>,
69
37
  ): Promise<void> {
70
- await resolveAnalytics()
71
- if (!trackFn) return
72
- return trackFn(context, eventName, data)
38
+ // Bind via static import so wrangler/esbuild bundles the target at build
39
+ // time. The previous variable-path dynamic import (await import(modulePath))
40
+ // could not be statically analyzed; in Cloudflare Workers the bundle did
41
+ // not include @growth-labs/analytics/utils, the runtime resolved null, and
42
+ // every auth event silently no-op'd — surfaced post-2026-05-22 as
43
+ // `signup_completed` / `login_completed` rows never landing in
44
+ // gl_conversion_events on the new Fronts Worker. See peerDependency.
45
+ await trackServerEvent(context as Parameters<typeof trackServerEvent>[0], eventName, data)
73
46
  }
74
47
 
75
48
  export function normalizeAuthProvider(
@@ -123,8 +96,9 @@ export async function trackAuthEvent(
123
96
  return emitAuthEvent(context, eventName, data)
124
97
  }
125
98
 
99
+ // Test-only export retained for compatibility with existing tests that reset
100
+ // internal state between runs. Now a no-op since there's no internal state
101
+ // to reset — trackFn is the bound static import.
126
102
  export function _resetAnalytics(): void {
127
- trackFn = null
128
- resolved = false
129
- resolvePromise = null
103
+ // noop
130
104
  }
@@ -1,7 +1,11 @@
1
1
  // Identity-bridge helpers. The OpenAuth issuer this package targets emits
2
- // `sub` claims in the form `user:<ULID>` (e.g. `user:01KP8KA31Z3AGRCC578R9HXW0H`).
3
- // The unprefixed bare-ULID form was used in early issuer releases and is
4
- // no longer the contract.
2
+ // `sub` claims in the form `user:<id>`, where `<id>` is the issuer's userId.
3
+ // Prod issuers emit a 36-char UUIDv4 (e.g.
4
+ // `user:000cf60e-b252-4b3d-8f1a-2c9e7d4a6b10`); early issuer releases used a
5
+ // 26-char Crockford ULID (e.g. `user:01KP8KA31Z3AGRCC578R9HXW0H`). Both
6
+ // suffix shapes are accepted — see the identity-join contract (canonical
7
+ // person key `user:<uuid>`). The unprefixed bare form was used in even
8
+ // earlier releases and is no longer the contract on the JWT `sub`.
5
9
  //
6
10
  // Two scenarios this module addresses:
7
11
  //
@@ -30,12 +34,29 @@
30
34
  export const IDENTITY_SUBJECT_PREFIX = 'user:'
31
35
 
32
36
  /**
33
- * Documented ULID shape: 26 chars of Crockford base32. The issuer
34
- * uses ULIDv1 and never falls back to a generic UUID — pin the
35
- * stricter pattern so drift is caught early.
37
+ * Documented ULID shape: 26 chars of Crockford base32. Used by early
38
+ * issuer releases.
36
39
  */
37
40
  const ULID_PATTERN = /^[0-9A-HJKMNP-TV-Z]{26}$/
38
41
 
42
+ /**
43
+ * Prod issuer shape: a 36-char UUIDv4, lower- or upper-case hex
44
+ * (`8-4-4-4-12`, version nibble `4`, variant nibble `8|9|a|b`). This is
45
+ * the canonical person-key suffix per the identity-join contract
46
+ * (`user:<uuid>`).
47
+ */
48
+ const UUIDV4_PATTERN =
49
+ /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/
50
+
51
+ /**
52
+ * The issuer's `sub` suffix is valid if it matches either the current
53
+ * prod UUIDv4 shape or the legacy 26-char ULID shape. Both are accepted
54
+ * for backward compatibility; genuinely-malformed suffixes still fail.
55
+ */
56
+ function isValidIdentitySuffix(id: string): boolean {
57
+ return UUIDV4_PATTERN.test(id) || ULID_PATTERN.test(id)
58
+ }
59
+
39
60
  /** Stable error code for sub-format drift. */
40
61
  export const IDENTITY_SUB_FORMAT_DRIFT = 'IDENTITY_SUB_FORMAT_DRIFT'
41
62
 
@@ -51,14 +72,15 @@ export class IdentitySubFormatDriftError extends Error {
51
72
  }
52
73
 
53
74
  /**
54
- * Validate and unwrap a `sub` claim. Returns the bare ULID portion
55
- * (the value Fronts stores in `users.identity_user_id` post-migration
56
- * is the prefixed form; the bare ULID is the convenient handle for
57
- * matching against business-side records that pre-date the prefix).
75
+ * Validate and unwrap a `sub` claim. Returns the raw id portion (the
76
+ * value after the `user:` prefix — a 36-char UUIDv4 in prod, or a
77
+ * 26-char ULID from legacy issuers). The value Fronts stores in
78
+ * `users.identity_user_id` post-migration is the prefixed form; the raw
79
+ * id is the canonical person key per the identity-join contract.
58
80
  *
59
81
  * Throws `IdentitySubFormatDriftError` when the subject does not match
60
- * `user:<ULID>`. The error code `IDENTITY_SUB_FORMAT_DRIFT` is stable
61
- * for observability matching.
82
+ * `user:<uuid>` or the legacy `user:<ULID>`. The error code
83
+ * `IDENTITY_SUB_FORMAT_DRIFT` is stable for observability matching.
62
84
  */
63
85
  export function extractIdentityId(sub: string): string {
64
86
  if (typeof sub !== 'string' || sub.length === 0) {
@@ -67,17 +89,18 @@ export function extractIdentityId(sub: string): string {
67
89
  if (!sub.startsWith(IDENTITY_SUBJECT_PREFIX)) {
68
90
  throw new IdentitySubFormatDriftError(
69
91
  `JWT sub claim "${sub}" is missing the required "${IDENTITY_SUBJECT_PREFIX}" prefix. ` +
70
- `The issuer contract pins the format as "${IDENTITY_SUBJECT_PREFIX}<ULID>". ` +
92
+ `The issuer contract pins the format as "${IDENTITY_SUBJECT_PREFIX}<uuid>" ` +
93
+ '(legacy issuers used a 26-char ULID). ' +
71
94
  'If your issuer was migrated from an older release, see ' +
72
95
  'packages-docs/auth-identity-bridge.md for the bridge-row migration helper.',
73
96
  sub,
74
97
  )
75
98
  }
76
99
  const id = sub.slice(IDENTITY_SUBJECT_PREFIX.length)
77
- if (!ULID_PATTERN.test(id)) {
100
+ if (!isValidIdentitySuffix(id)) {
78
101
  throw new IdentitySubFormatDriftError(
79
102
  `JWT sub claim "${sub}" has the "${IDENTITY_SUBJECT_PREFIX}" prefix but the suffix ` +
80
- 'is not a valid 26-char Crockford base32 ULID.',
103
+ 'is not a valid 36-char UUIDv4 nor a legacy 26-char Crockford base32 ULID.',
81
104
  sub,
82
105
  )
83
106
  }
@@ -86,7 +109,8 @@ export function extractIdentityId(sub: string): string {
86
109
 
87
110
  /**
88
111
  * Convenience: normalize either form to the canonical prefixed `sub`.
89
- * Idempotent on already-prefixed input. Throws on invalid ULID.
112
+ * Idempotent on already-prefixed input. Accepts both a 36-char UUIDv4
113
+ * and a legacy 26-char ULID suffix; throws on a genuinely-malformed id.
90
114
  *
91
115
  * Useful inside a one-shot migration: pass each row's stored
92
116
  * `identity_user_id` through this and write back the result.
@@ -97,23 +121,58 @@ export function normalizeIdentitySubject(value: string): string {
97
121
  }
98
122
  if (value.startsWith(IDENTITY_SUBJECT_PREFIX)) {
99
123
  const id = value.slice(IDENTITY_SUBJECT_PREFIX.length)
100
- if (!ULID_PATTERN.test(id)) {
124
+ if (!isValidIdentitySuffix(id)) {
101
125
  throw new IdentitySubFormatDriftError(
102
- `value "${value}" has the "${IDENTITY_SUBJECT_PREFIX}" prefix but the suffix is not a valid ULID.`,
126
+ `value "${value}" has the "${IDENTITY_SUBJECT_PREFIX}" prefix but the suffix is not a valid UUIDv4 or ULID.`,
103
127
  value,
104
128
  )
105
129
  }
106
130
  return value
107
131
  }
108
- if (!ULID_PATTERN.test(value)) {
132
+ if (!isValidIdentitySuffix(value)) {
109
133
  throw new IdentitySubFormatDriftError(
110
- `value "${value}" is neither a prefixed "${IDENTITY_SUBJECT_PREFIX}<ULID>" nor a valid bare ULID.`,
134
+ `value "${value}" is neither a prefixed "${IDENTITY_SUBJECT_PREFIX}<uuid>" nor a valid bare UUIDv4 / ULID.`,
111
135
  value,
112
136
  )
113
137
  }
114
138
  return `${IDENTITY_SUBJECT_PREFIX}${value}`
115
139
  }
116
140
 
141
+ /**
142
+ * Identity-join contract §3/§4 (OQ-3, login-time stamping): the auth
143
+ * callback holds both the subject and the hash when it mints the `gl_id`
144
+ * cookie, so it is the strongest place to stamp the analytics→user
145
+ * translation key — it emits a server analytics event carrying the
146
+ * subject, and `@growth-labs/analytics` re-derives the same hash and
147
+ * stamps `gl_identity_links.identity_user_id` with the raw `<uuid>`.
148
+ *
149
+ * For that round-trip to hold, the subject auth emits MUST be the exact
150
+ * **prefixed** `user:<uuid>` string the cookie hash is computed over
151
+ * (contract §3 pins `identity_id = SHA-256(subject + secret)` where
152
+ * `subject = "user:" + <uuid>`). `@growth-labs/analytics`'s
153
+ * `deriveOpaqueIdentityId` hashes the value verbatim and then strips a
154
+ * single leading `user:` to store the raw uuid in the column — so the
155
+ * canonical prefixed form is what must travel on the wire.
156
+ *
157
+ * This helper coerces the issuer's `sub` to that canonical prefixed
158
+ * form via `normalizeIdentitySubject` (UUIDv4-aware after #127). It is
159
+ * deliberately **non-throwing**: a genuinely-drifted subject must never
160
+ * break login, so on a validation failure it returns the original
161
+ * subject unchanged. The cookie producer (`createIdentityCookiePayload`)
162
+ * and this emit hash the *same* string in every case, so the
163
+ * events↔links join stays consistent even on the fallback path; the
164
+ * normalization only adds the forward guarantee that a bare-uuid subject
165
+ * is prefixed before hashing so the analytics column gets the right raw
166
+ * value.
167
+ */
168
+ export function canonicalIdentitySubject(sub: string): string {
169
+ try {
170
+ return normalizeIdentitySubject(sub)
171
+ } catch {
172
+ return sub
173
+ }
174
+ }
175
+
117
176
  // ─── Bridge migration helpers ───
118
177
 
119
178
  export interface BridgeMigrationOptions {
@@ -15,6 +15,7 @@ export {
15
15
  type BridgeMigrationStatement,
16
16
  buildLegacyIdentitySubjectCanary,
17
17
  buildLegacyIdentitySubjectMigration,
18
+ canonicalIdentitySubject,
18
19
  extractIdentityId,
19
20
  findLegacyIdentitySubjects,
20
21
  IDENTITY_SUB_FORMAT_DRIFT,