@growth-labs/auth 0.7.5 → 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.
- package/dist/routes/api/shared.d.ts.map +1 -1
- package/dist/routes/api/shared.js +7 -2
- package/dist/routes/api/shared.js.map +1 -1
- package/dist/routes/callback.d.ts.map +1 -1
- package/dist/routes/callback.js +15 -2
- package/dist/routes/callback.js.map +1 -1
- package/dist/utils/identity.d.ts +37 -7
- package/dist/utils/identity.d.ts.map +1 -1
- package/dist/utils/identity.js +76 -20
- package/dist/utils/identity.js.map +1 -1
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -1
- package/dist/utils/index.js.map +1 -1
- package/package.json +2 -2
- package/src/routes/api/shared.ts +7 -2
- package/src/routes/callback.ts +15 -2
- package/src/utils/identity.ts +79 -20
- package/src/utils/index.ts +1 -0
|
@@ -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;
|
|
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
|
-
|
|
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:
|
|
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":"
|
|
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"}
|
package/dist/routes/callback.js
CHANGED
|
@@ -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(
|
|
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:
|
|
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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"}
|
package/dist/utils/identity.d.ts
CHANGED
|
@@ -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
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
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
|
|
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.
|
|
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":"
|
|
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"}
|
package/dist/utils/identity.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
// Identity-bridge helpers. The OpenAuth issuer this package targets emits
|
|
2
|
-
// `sub` claims in the form `user:<
|
|
3
|
-
//
|
|
4
|
-
//
|
|
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.
|
|
32
|
-
*
|
|
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
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
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
|
|
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}<
|
|
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 (!
|
|
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.
|
|
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 (!
|
|
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 (!
|
|
93
|
-
throw new IdentitySubFormatDriftError(`value "${value}" is neither a prefixed "${IDENTITY_SUBJECT_PREFIX}<
|
|
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":"AAAA,0EAA0E;AAC1E,
|
|
1
|
+
{"version":3,"file":"identity.js","sourceRoot":"","sources":["../../src/utils/identity.ts"],"names":[],"mappings":"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"}
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -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"}
|
package/dist/utils/index.js
CHANGED
|
@@ -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';
|
package/dist/utils/index.js.map
CHANGED
|
@@ -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.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"types": "./dist/index.d.ts",
|
|
6
6
|
"exports": {
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"typescript": "^5.8.0",
|
|
58
58
|
"vite": "^7.3.2",
|
|
59
59
|
"vitest": "^3.0.0",
|
|
60
|
-
"@growth-labs/analytics": "0.
|
|
60
|
+
"@growth-labs/analytics": "0.8.0"
|
|
61
61
|
},
|
|
62
62
|
"scripts": {
|
|
63
63
|
"build": "tsc",
|
package/src/routes/api/shared.ts
CHANGED
|
@@ -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
|
-
|
|
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:
|
|
189
|
+
identityUserId: identitySubject,
|
|
185
190
|
}),
|
|
186
191
|
)
|
|
187
192
|
}
|
package/src/routes/callback.ts
CHANGED
|
@@ -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(
|
|
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:
|
|
177
|
+
identityUserId: identitySubject,
|
|
165
178
|
})
|
|
166
179
|
resolveWaitUntil(context)(promise)
|
|
167
180
|
}
|
package/src/utils/identity.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
// Identity-bridge helpers. The OpenAuth issuer this package targets emits
|
|
2
|
-
// `sub` claims in the form `user:<
|
|
3
|
-
//
|
|
4
|
-
//
|
|
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.
|
|
34
|
-
*
|
|
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
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
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
|
|
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}<
|
|
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 (!
|
|
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.
|
|
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 (!
|
|
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 (!
|
|
132
|
+
if (!isValidIdentitySuffix(value)) {
|
|
109
133
|
throw new IdentitySubFormatDriftError(
|
|
110
|
-
`value "${value}" is neither a prefixed "${IDENTITY_SUBJECT_PREFIX}<
|
|
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 {
|
package/src/utils/index.ts
CHANGED