@corbado/observe 1.14.0 → 1.15.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/CHANGELOG.md +25 -0
- package/dist/analysis/config.mjs +20 -1
- package/dist/analysis/device.mjs +4 -2
- package/dist/analysis/index.mjs +11 -0
- package/dist/analysis/low-events/grouped-input-tracker.mjs +2 -9
- package/dist/analysis/low-events/input-tracker.mjs +2 -9
- package/dist/analysis/plugins/device-handle-max-age.mjs +8 -0
- package/dist/analysis/queue.mjs +3 -28
- package/dist/analysis/readers/input-paste.mjs +16 -23
- package/dist/analysis/readers/page.mjs +4 -1
- package/dist/analysis/store/event-store.mjs +24 -1
- package/dist/analysis/tracker-core.mjs +30 -19
- package/dist/analysis/utils/email-link-continuity.mjs +6 -1
- package/dist/analysis/utils/source-path.mjs +23 -1
- package/dist/analysis/utils/webauthn-ceremony.mjs +279 -0
- package/dist/analysis/utils/webauthn-payloads.mjs +11 -5
- package/dist/analysis/version.mjs +1 -1
- package/dist/cdn/script.global.js +2 -2
- package/dist/index.d.mts +85 -23
- package/dist/index.d.ts +85 -23
- package/dist/index.js +4 -4
- package/dist/index.mjs +4 -4
- package/dist/manifest/index.d.mts +270 -0
- package/dist/manifest/index.d.ts +270 -0
- package/dist/manifest/index.js +781 -0
- package/dist/manifest/index.mjs +734 -0
- package/manifest/package.json +8 -0
- package/package.json +4 -2
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
// src/utils/webauthn-ceremony.ts
|
|
2
|
+
var TRANSPORTS = [1];
|
|
3
|
+
var CREDENTIAL_DESCRIPTOR = { type: 1, id: "credentialId", transports: TRANSPORTS };
|
|
4
|
+
var EXTENSION_OUTPUTS = {
|
|
5
|
+
appid: 1,
|
|
6
|
+
appidExclude: 1,
|
|
7
|
+
credProps: { rk: 1 },
|
|
8
|
+
hmacCreateSecret: 1,
|
|
9
|
+
hmacGetSecret: {},
|
|
10
|
+
largeBlob: { supported: 1, written: 1 },
|
|
11
|
+
prf: { enabled: 1 }
|
|
12
|
+
};
|
|
13
|
+
var REQUEST_OPTIONS = {
|
|
14
|
+
challenge: "zero",
|
|
15
|
+
timeout: 1,
|
|
16
|
+
rpId: 1,
|
|
17
|
+
allowCredentials: [CREDENTIAL_DESCRIPTOR],
|
|
18
|
+
userVerification: 1,
|
|
19
|
+
hints: [1],
|
|
20
|
+
extensions: "names"
|
|
21
|
+
};
|
|
22
|
+
var CREATION_OPTIONS = {
|
|
23
|
+
rp: { id: 1 },
|
|
24
|
+
user: { id: "userId" },
|
|
25
|
+
challenge: "zero",
|
|
26
|
+
pubKeyCredParams: [{ type: 1, alg: 1 }],
|
|
27
|
+
timeout: 1,
|
|
28
|
+
excludeCredentials: [CREDENTIAL_DESCRIPTOR],
|
|
29
|
+
authenticatorSelection: { authenticatorAttachment: 1, requireResidentKey: 1, residentKey: 1, userVerification: 1 },
|
|
30
|
+
attestation: 1,
|
|
31
|
+
attestationFormats: [1],
|
|
32
|
+
hints: [1],
|
|
33
|
+
extensions: "names"
|
|
34
|
+
};
|
|
35
|
+
var ASSERTION_RESPONSE = {
|
|
36
|
+
id: "credentialId",
|
|
37
|
+
rawId: "credentialId",
|
|
38
|
+
type: 1,
|
|
39
|
+
authenticatorAttachment: 1,
|
|
40
|
+
response: { clientDataJSON: "clientData", authenticatorData: "authData", userHandle: "userHandle" },
|
|
41
|
+
clientExtensionResults: EXTENSION_OUTPUTS
|
|
42
|
+
};
|
|
43
|
+
var CREATION_RESPONSE = {
|
|
44
|
+
id: "credentialId",
|
|
45
|
+
rawId: "credentialId",
|
|
46
|
+
type: 1,
|
|
47
|
+
authenticatorAttachment: 1,
|
|
48
|
+
response: {
|
|
49
|
+
clientDataJSON: "clientData",
|
|
50
|
+
attestationObject: "authData",
|
|
51
|
+
authenticatorData: "authData",
|
|
52
|
+
transports: TRANSPORTS,
|
|
53
|
+
publicKeyAlgorithm: 1
|
|
54
|
+
},
|
|
55
|
+
clientExtensionResults: EXTENSION_OUTPUTS
|
|
56
|
+
};
|
|
57
|
+
var METHODS = {
|
|
58
|
+
clientExtensionResults: "getClientExtensionResults",
|
|
59
|
+
transports: "getTransports",
|
|
60
|
+
authenticatorData: "getAuthenticatorData",
|
|
61
|
+
publicKeyAlgorithm: "getPublicKeyAlgorithm"
|
|
62
|
+
};
|
|
63
|
+
var CLIENT_DATA_KEYS = ["type", "challenge", "origin", "crossOrigin", "topOrigin"];
|
|
64
|
+
var USER_HANDLE_PLACEHOLDER = "AAAAAAAAAAAAAAAAAAAAAA";
|
|
65
|
+
var MIN_SWAP_BYTES = 16;
|
|
66
|
+
var MAX_STAND_INS = 256;
|
|
67
|
+
var MAX_LIST = 1e3;
|
|
68
|
+
var standIns = /* @__PURE__ */ new Map();
|
|
69
|
+
function ceremonyCreationOptions(value, options) {
|
|
70
|
+
return projectOptions(value, CREATION_OPTIONS, options);
|
|
71
|
+
}
|
|
72
|
+
function ceremonyRequestOptions(value, options) {
|
|
73
|
+
return projectOptions(value, REQUEST_OPTIONS, options);
|
|
74
|
+
}
|
|
75
|
+
function ceremonyCreationResponse(value, options) {
|
|
76
|
+
return projectCredential(value, CREATION_RESPONSE, options);
|
|
77
|
+
}
|
|
78
|
+
function ceremonyAssertionResponse(value, options) {
|
|
79
|
+
return projectCredential(value, ASSERTION_RESPONSE, options);
|
|
80
|
+
}
|
|
81
|
+
function context(options) {
|
|
82
|
+
return { passkeyReference: options?.passkeyReference === true, projectors: options ?? {} };
|
|
83
|
+
}
|
|
84
|
+
function projectOptions(input, rules, options) {
|
|
85
|
+
try {
|
|
86
|
+
const value = parse(input);
|
|
87
|
+
if (!value) return void 0;
|
|
88
|
+
const ctx = context(options);
|
|
89
|
+
const inner = value.challenge === void 0 ? asRecord(read(value, "publicKey")) : void 0;
|
|
90
|
+
if (inner) return JSON.stringify({ publicKey: project(inner, rules, ctx) });
|
|
91
|
+
return JSON.stringify(project(value, rules, ctx));
|
|
92
|
+
} catch {
|
|
93
|
+
return void 0;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
function projectCredential(input, rules, options) {
|
|
97
|
+
try {
|
|
98
|
+
const value = parse(input);
|
|
99
|
+
if (!value) return void 0;
|
|
100
|
+
const ctx = context(options);
|
|
101
|
+
if (!ctx.passkeyReference) {
|
|
102
|
+
const from = decode(read(value, "rawId")) ?? decode(read(value, "id"));
|
|
103
|
+
if (from && from.length >= MIN_SWAP_BYTES) ctx.swap = { from, to: standIn(from) };
|
|
104
|
+
}
|
|
105
|
+
return JSON.stringify(project(value, rules, ctx));
|
|
106
|
+
} catch {
|
|
107
|
+
return void 0;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
function project(value, rules, ctx, depth = 0) {
|
|
111
|
+
if (depth > 8) throw new TypeError("Invalid telemetry payload");
|
|
112
|
+
const out = /* @__PURE__ */ Object.create(null);
|
|
113
|
+
for (const key of Object.keys(rules)) {
|
|
114
|
+
let item = read(value, key);
|
|
115
|
+
const method = METHODS[key];
|
|
116
|
+
if (item === void 0 && method && typeof value[method] === "function") {
|
|
117
|
+
item = Reflect.apply(value[method], value, []);
|
|
118
|
+
}
|
|
119
|
+
if (item === void 0) continue;
|
|
120
|
+
const result = apply(item, rules[key], ctx, depth + 1);
|
|
121
|
+
if (result !== void 0) out[key] = result;
|
|
122
|
+
}
|
|
123
|
+
return out;
|
|
124
|
+
}
|
|
125
|
+
function apply(item, rule, ctx, depth) {
|
|
126
|
+
if (item === null) return rule === "userId" || rule === "userHandle" || rule === 1 ? null : void 0;
|
|
127
|
+
switch (rule) {
|
|
128
|
+
case 1:
|
|
129
|
+
if (typeof item === "string" || typeof item === "boolean") return item;
|
|
130
|
+
if (typeof item === "number") return Number.isFinite(item) ? item : null;
|
|
131
|
+
return isBinary(item) ? base64url(toBytes(item)) : void 0;
|
|
132
|
+
case "zero":
|
|
133
|
+
return zero(item);
|
|
134
|
+
case "credentialId":
|
|
135
|
+
return credentialId(item, ctx);
|
|
136
|
+
case "userHandle":
|
|
137
|
+
case "userId":
|
|
138
|
+
return userIdentifier(item, rule, ctx);
|
|
139
|
+
case "clientData":
|
|
140
|
+
return clientData(item);
|
|
141
|
+
case "authData":
|
|
142
|
+
return authData(item, ctx);
|
|
143
|
+
case "names": {
|
|
144
|
+
const record2 = asRecord(item);
|
|
145
|
+
if (!record2) return void 0;
|
|
146
|
+
const names = /* @__PURE__ */ Object.create(null);
|
|
147
|
+
for (const key of Object.keys(record2)) names[key] = true;
|
|
148
|
+
return names;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (Array.isArray(rule)) {
|
|
152
|
+
return Array.isArray(item) ? item.slice(0, MAX_LIST).map((entry) => apply(entry, rule[0], ctx, depth) ?? null) : void 0;
|
|
153
|
+
}
|
|
154
|
+
const record = asRecord(item);
|
|
155
|
+
return record ? project(record, rule, ctx, depth) : void 0;
|
|
156
|
+
}
|
|
157
|
+
function zero(item) {
|
|
158
|
+
if (typeof item === "string") return item.replace(/[^=]/g, "A");
|
|
159
|
+
return isBinary(item) ? base64url(new Uint8Array(toBytes(item).length)) : void 0;
|
|
160
|
+
}
|
|
161
|
+
function credentialId(item, ctx) {
|
|
162
|
+
if (ctx.passkeyReference) {
|
|
163
|
+
if (typeof item === "string") return item;
|
|
164
|
+
return isBinary(item) ? base64url(toBytes(item)) : void 0;
|
|
165
|
+
}
|
|
166
|
+
const bytes = decode(item);
|
|
167
|
+
return bytes ? base64url(standIn(bytes)) : void 0;
|
|
168
|
+
}
|
|
169
|
+
function userIdentifier(item, rule, ctx) {
|
|
170
|
+
const identifier = typeof item === "string" ? item : isBinary(item) ? base64url(toBytes(item)) : void 0;
|
|
171
|
+
if (identifier === void 0) return void 0;
|
|
172
|
+
const projector = ctx.projectors.userId;
|
|
173
|
+
if (projector) {
|
|
174
|
+
try {
|
|
175
|
+
const output = projector(identifier);
|
|
176
|
+
return typeof output === "string" || output === null ? output : void 0;
|
|
177
|
+
} catch {
|
|
178
|
+
return void 0;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
if (rule === "userId") return void 0;
|
|
182
|
+
return identifier === "" ? "" : USER_HANDLE_PLACEHOLDER;
|
|
183
|
+
}
|
|
184
|
+
function clientData(item) {
|
|
185
|
+
const bytes = decode(item);
|
|
186
|
+
if (!bytes) return void 0;
|
|
187
|
+
let parsed;
|
|
188
|
+
try {
|
|
189
|
+
parsed = asRecord(JSON.parse(new TextDecoder().decode(bytes)));
|
|
190
|
+
} catch {
|
|
191
|
+
return void 0;
|
|
192
|
+
}
|
|
193
|
+
if (!parsed) return void 0;
|
|
194
|
+
const out = {};
|
|
195
|
+
for (const key of CLIENT_DATA_KEYS) {
|
|
196
|
+
const member = parsed[key];
|
|
197
|
+
if (member === void 0) continue;
|
|
198
|
+
if (key === "challenge") {
|
|
199
|
+
if (typeof member === "string") out[key] = zero(member);
|
|
200
|
+
} else if (typeof member === "string" || typeof member === "boolean") {
|
|
201
|
+
out[key] = member;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return base64url(new TextEncoder().encode(JSON.stringify(out)));
|
|
205
|
+
}
|
|
206
|
+
function authData(item, ctx) {
|
|
207
|
+
const bytes = decode(item);
|
|
208
|
+
if (!bytes) return void 0;
|
|
209
|
+
if (!ctx.swap) return base64url(bytes);
|
|
210
|
+
const { from, to } = ctx.swap;
|
|
211
|
+
const out = bytes.slice();
|
|
212
|
+
for (let i = 0; i + from.length <= out.length; i++) {
|
|
213
|
+
let match = true;
|
|
214
|
+
for (let j = 0; j < from.length; j++) {
|
|
215
|
+
if (out[i + j] !== from[j]) {
|
|
216
|
+
match = false;
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
if (match) {
|
|
221
|
+
out.set(to, i);
|
|
222
|
+
i += from.length - 1;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return base64url(out);
|
|
226
|
+
}
|
|
227
|
+
function standIn(id) {
|
|
228
|
+
const key = base64url(id);
|
|
229
|
+
const known = standIns.get(key);
|
|
230
|
+
if (known) return known;
|
|
231
|
+
const fresh = new Uint8Array(id.length);
|
|
232
|
+
if (typeof crypto !== "undefined" && typeof crypto.getRandomValues === "function") {
|
|
233
|
+
crypto.getRandomValues(fresh);
|
|
234
|
+
} else {
|
|
235
|
+
for (let i = 0; i < fresh.length; i++) fresh[i] = Math.floor(Math.random() * 256);
|
|
236
|
+
}
|
|
237
|
+
if (standIns.size < MAX_STAND_INS) standIns.set(key, fresh);
|
|
238
|
+
return fresh;
|
|
239
|
+
}
|
|
240
|
+
function parse(input) {
|
|
241
|
+
return asRecord(typeof input === "string" ? JSON.parse(input) : input);
|
|
242
|
+
}
|
|
243
|
+
function read(value, key) {
|
|
244
|
+
return value[key];
|
|
245
|
+
}
|
|
246
|
+
function asRecord(value) {
|
|
247
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) && !isBinary(value) ? value : void 0;
|
|
248
|
+
}
|
|
249
|
+
function isBinary(value) {
|
|
250
|
+
return value instanceof ArrayBuffer || ArrayBuffer.isView(value);
|
|
251
|
+
}
|
|
252
|
+
function toBytes(value) {
|
|
253
|
+
return value instanceof ArrayBuffer ? new Uint8Array(value) : new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
|
|
254
|
+
}
|
|
255
|
+
function decode(value) {
|
|
256
|
+
if (isBinary(value)) return toBytes(value);
|
|
257
|
+
if (typeof value !== "string") return void 0;
|
|
258
|
+
try {
|
|
259
|
+
const normalized = value.replace(/-/g, "+").replace(/_/g, "/").replace(/=+$/, "");
|
|
260
|
+
const binary = atob(normalized + "===".slice((normalized.length + 3) % 4));
|
|
261
|
+
const bytes = new Uint8Array(binary.length);
|
|
262
|
+
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
|
|
263
|
+
return bytes;
|
|
264
|
+
} catch {
|
|
265
|
+
return void 0;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
function base64url(bytes) {
|
|
269
|
+
let binary = "";
|
|
270
|
+
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
271
|
+
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
|
|
272
|
+
}
|
|
273
|
+
export {
|
|
274
|
+
USER_HANDLE_PLACEHOLDER,
|
|
275
|
+
ceremonyAssertionResponse,
|
|
276
|
+
ceremonyCreationOptions,
|
|
277
|
+
ceremonyCreationResponse,
|
|
278
|
+
ceremonyRequestOptions
|
|
279
|
+
};
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
// src/utils/webauthn-payloads.ts
|
|
2
|
+
import {
|
|
3
|
+
ceremonyAssertionResponse,
|
|
4
|
+
ceremonyCreationOptions,
|
|
5
|
+
ceremonyCreationResponse,
|
|
6
|
+
ceremonyRequestOptions
|
|
7
|
+
} from "./webauthn-ceremony.mjs";
|
|
2
8
|
var BLACKLIST = {
|
|
3
9
|
creationOptions: ["/user/name", "/user/displayName"],
|
|
4
10
|
creationResponse: ["/clientExtensionResults/prf/results"],
|
|
@@ -6,16 +12,16 @@ var BLACKLIST = {
|
|
|
6
12
|
assertionResponse: ["/response/signature", "/clientExtensionResults/prf/results"]
|
|
7
13
|
};
|
|
8
14
|
function sanitizeCreationOptions(value, projectors) {
|
|
9
|
-
return sanitize(value, "creationOptions", projectors);
|
|
15
|
+
return typeof __CBO_DATA_CEREMONY__ === "boolean" && __CBO_DATA_CEREMONY__ ? ceremonyCreationOptions(value, projectors) : sanitize(value, "creationOptions", projectors);
|
|
10
16
|
}
|
|
11
17
|
function sanitizeCreationResponse(value, projectors) {
|
|
12
|
-
return sanitize(value, "creationResponse", projectors);
|
|
18
|
+
return typeof __CBO_DATA_CEREMONY__ === "boolean" && __CBO_DATA_CEREMONY__ ? ceremonyCreationResponse(value, projectors) : sanitize(value, "creationResponse", projectors);
|
|
13
19
|
}
|
|
14
|
-
function sanitizeRequestOptions(value) {
|
|
15
|
-
return sanitize(value, "requestOptions");
|
|
20
|
+
function sanitizeRequestOptions(value, options) {
|
|
21
|
+
return typeof __CBO_DATA_CEREMONY__ === "boolean" && __CBO_DATA_CEREMONY__ ? ceremonyRequestOptions(value, options) : sanitize(value, "requestOptions");
|
|
16
22
|
}
|
|
17
23
|
function sanitizeAssertionResponse(value, projectors) {
|
|
18
|
-
return sanitize(value, "assertionResponse", projectors);
|
|
24
|
+
return typeof __CBO_DATA_CEREMONY__ === "boolean" && __CBO_DATA_CEREMONY__ ? ceremonyAssertionResponse(value, projectors) : sanitize(value, "assertionResponse", projectors);
|
|
19
25
|
}
|
|
20
26
|
function sanitize(input, kind, projectors = {}) {
|
|
21
27
|
try {
|