vesant-sdk 1.7.0 → 1.7.1-dev.0520b46
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/{client-B0qhE2kr.d.mts → client-5q4whhGu.d.mts} +2 -2
- package/dist/{client-DF7hlMEz.d.ts → client-BaUekQT8.d.ts} +96 -52
- package/dist/{client-DtH2RLuy.d.ts → client-DUHC-68_.d.ts} +2 -2
- package/dist/{client-DrjgZoH_.d.mts → client-XDEhh9VN.d.mts} +96 -52
- package/dist/{client-BolQlL5e.d.mts → client-q9fN8Hhf.d.mts} +103 -1
- package/dist/{client-BolQlL5e.d.ts → client-q9fN8Hhf.d.ts} +103 -1
- package/dist/compliance/index.d.mts +3 -3
- package/dist/compliance/index.d.ts +3 -3
- package/dist/compliance/index.js +218 -90
- package/dist/compliance/index.js.map +1 -1
- package/dist/compliance/index.mjs +218 -90
- package/dist/compliance/index.mjs.map +1 -1
- package/dist/decisions/index.d.mts +1 -1
- package/dist/decisions/index.d.ts +1 -1
- package/dist/decisions/index.js +69 -8
- package/dist/decisions/index.js.map +1 -1
- package/dist/decisions/index.mjs +69 -8
- package/dist/decisions/index.mjs.map +1 -1
- package/dist/geolocation/index.d.mts +68 -7
- package/dist/geolocation/index.d.ts +68 -7
- package/dist/geolocation/index.js +223 -90
- package/dist/geolocation/index.js.map +1 -1
- package/dist/geolocation/index.mjs +220 -91
- package/dist/geolocation/index.mjs.map +1 -1
- package/dist/index-C3zUKydT.d.mts +264 -0
- package/dist/index-DSDgS09k.d.ts +264 -0
- package/dist/index.d.mts +27 -10
- package/dist/index.d.ts +27 -10
- package/dist/index.js +418 -126
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +409 -127
- package/dist/index.mjs.map +1 -1
- package/dist/kyc/core.d.mts +2 -2
- package/dist/kyc/core.d.ts +2 -2
- package/dist/kyc/core.js +102 -21
- package/dist/kyc/core.js.map +1 -1
- package/dist/kyc/core.mjs +102 -22
- package/dist/kyc/core.mjs.map +1 -1
- package/dist/kyc/index.d.mts +154 -30
- package/dist/kyc/index.d.ts +154 -30
- package/dist/kyc/index.js +102 -21
- package/dist/kyc/index.js.map +1 -1
- package/dist/kyc/index.mjs +102 -22
- package/dist/kyc/index.mjs.map +1 -1
- package/dist/react.d.mts +14 -3
- package/dist/react.d.ts +14 -3
- package/dist/react.js +317 -68
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +317 -68
- package/dist/react.mjs.map +1 -1
- package/dist/risk-profile/index.d.mts +1 -1
- package/dist/risk-profile/index.d.ts +1 -1
- package/dist/risk-profile/index.js +69 -8
- package/dist/risk-profile/index.js.map +1 -1
- package/dist/risk-profile/index.mjs +69 -8
- package/dist/risk-profile/index.mjs.map +1 -1
- package/dist/scores/index.d.mts +1 -1
- package/dist/scores/index.d.ts +1 -1
- package/dist/scores/index.js +69 -8
- package/dist/scores/index.js.map +1 -1
- package/dist/scores/index.mjs +69 -8
- package/dist/scores/index.mjs.map +1 -1
- package/dist/tax/index.d.mts +2 -2
- package/dist/tax/index.d.ts +2 -2
- package/dist/tax/index.js +74 -10
- package/dist/tax/index.js.map +1 -1
- package/dist/tax/index.mjs +74 -10
- package/dist/tax/index.mjs.map +1 -1
- package/dist/webhooks/index.d.mts +2 -189
- package/dist/webhooks/index.d.ts +2 -189
- package/dist/webhooks/index.js +85 -21
- package/dist/webhooks/index.js.map +1 -1
- package/dist/webhooks/index.mjs +85 -22
- package/dist/webhooks/index.mjs.map +1 -1
- package/package.json +4 -1
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
// src/core/auth.ts
|
|
2
|
+
var StaticApiKeyProvider = class {
|
|
3
|
+
constructor(apiKey) {
|
|
4
|
+
this.apiKey = apiKey;
|
|
5
|
+
}
|
|
6
|
+
async getCredential() {
|
|
7
|
+
return this.apiKey ? { scheme: "Bearer", token: this.apiKey } : null;
|
|
8
|
+
}
|
|
9
|
+
canRefresh() {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
|
|
1
14
|
// src/core/errors.ts
|
|
2
15
|
var VesantError = class _VesantError extends Error {
|
|
3
16
|
constructor(message, code, statusCode, details) {
|
|
@@ -214,7 +227,7 @@ function createConsoleLogger() {
|
|
|
214
227
|
}
|
|
215
228
|
|
|
216
229
|
// src/core/version.ts
|
|
217
|
-
var SDK_VERSION = "1.7.
|
|
230
|
+
var SDK_VERSION = "1.7.1";
|
|
218
231
|
|
|
219
232
|
// src/shared/browser-utils.ts
|
|
220
233
|
function generateUUID() {
|
|
@@ -296,6 +309,12 @@ var BaseClient = class {
|
|
|
296
309
|
if (!config.tenantId?.trim()) {
|
|
297
310
|
throw new ValidationError("tenantId is required and must be a non-empty string", ["tenantId"]);
|
|
298
311
|
}
|
|
312
|
+
if (typeof fetch === "undefined") {
|
|
313
|
+
throw new VesantError(
|
|
314
|
+
"The Vesant SDK requires a global fetch (Node.js >= 18). Upgrade Node or provide a fetch polyfill.",
|
|
315
|
+
"RUNTIME_UNSUPPORTED"
|
|
316
|
+
);
|
|
317
|
+
}
|
|
299
318
|
this.interceptors = config.interceptors || [];
|
|
300
319
|
this.logger = config.logger || createConsoleLogger();
|
|
301
320
|
let environment = config.environment;
|
|
@@ -314,11 +333,12 @@ var BaseClient = class {
|
|
|
314
333
|
environment,
|
|
315
334
|
headers: config.headers || {},
|
|
316
335
|
timeout: config.timeout || 1e4,
|
|
317
|
-
retries: config.retries
|
|
336
|
+
retries: config.retries ?? 3,
|
|
318
337
|
debug: config.debug || false,
|
|
319
338
|
interceptors: this.interceptors,
|
|
320
339
|
logger: this.logger
|
|
321
340
|
};
|
|
341
|
+
this.authProvider = config.authProvider ?? (apiKey ? new StaticApiKeyProvider(apiKey) : void 0);
|
|
322
342
|
if (config.circuitBreaker) {
|
|
323
343
|
this.circuitBreaker = new CircuitBreaker(config.circuitBreaker);
|
|
324
344
|
}
|
|
@@ -326,6 +346,19 @@ var BaseClient = class {
|
|
|
326
346
|
this.rateLimitTracker = new RateLimitTracker();
|
|
327
347
|
}
|
|
328
348
|
}
|
|
349
|
+
/**
|
|
350
|
+
* Resolve the Idempotency-Key for a request. Mutating methods (POST/PUT/PATCH)
|
|
351
|
+
* get the caller-supplied key or a freshly minted UUID; non-mutating methods get
|
|
352
|
+
* none. requestWithRetry resolves this once and injects it so every retry reuses
|
|
353
|
+
* one key (letting the server dedup replays); a direct request() resolves per call.
|
|
354
|
+
*/
|
|
355
|
+
resolveIdempotencyKey(method, requestOptions) {
|
|
356
|
+
const isMutating = ["POST", "PUT", "PATCH"].includes((method || "GET").toUpperCase());
|
|
357
|
+
if (!isMutating) {
|
|
358
|
+
return void 0;
|
|
359
|
+
}
|
|
360
|
+
return requestOptions?.idempotencyKey ?? generateUUID();
|
|
361
|
+
}
|
|
329
362
|
/**
|
|
330
363
|
* Make an HTTP request with timeout and error handling
|
|
331
364
|
*/
|
|
@@ -351,15 +384,20 @@ var BaseClient = class {
|
|
|
351
384
|
...this.config.headers,
|
|
352
385
|
...options.headers || {}
|
|
353
386
|
};
|
|
354
|
-
|
|
355
|
-
|
|
387
|
+
const credential = await this.authProvider?.getCredential({
|
|
388
|
+
tenantId: this.config.tenantId,
|
|
389
|
+
requestId
|
|
390
|
+
});
|
|
391
|
+
if (credential) {
|
|
392
|
+
headers["Authorization"] = `${credential.scheme ?? "Bearer"} ${credential.token}`;
|
|
356
393
|
}
|
|
357
394
|
if (this.config.environment === "sandbox") {
|
|
358
395
|
headers["X-Sandbox"] = "true";
|
|
359
396
|
}
|
|
360
397
|
const method = (options.method || "GET").toUpperCase();
|
|
361
|
-
|
|
362
|
-
|
|
398
|
+
const idempotencyKey = this.resolveIdempotencyKey(method, requestOptions);
|
|
399
|
+
if (idempotencyKey) {
|
|
400
|
+
headers["Idempotency-Key"] = idempotencyKey;
|
|
363
401
|
}
|
|
364
402
|
const controller = new AbortController();
|
|
365
403
|
const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
|
|
@@ -380,10 +418,28 @@ var BaseClient = class {
|
|
|
380
418
|
if (this.config.debug) {
|
|
381
419
|
this.logger.debug(`${finalOptions.method || "GET"} ${endpoint}`);
|
|
382
420
|
}
|
|
383
|
-
|
|
421
|
+
let response = await fetch(url, {
|
|
384
422
|
...finalOptions,
|
|
385
423
|
signal: controller.signal
|
|
386
424
|
});
|
|
425
|
+
if (response.status === 401 && this.authProvider?.canRefresh?.()) {
|
|
426
|
+
const refreshed = await this.authProvider.getCredential({
|
|
427
|
+
tenantId: this.config.tenantId,
|
|
428
|
+
requestId,
|
|
429
|
+
forceRefresh: true
|
|
430
|
+
});
|
|
431
|
+
if (refreshed) {
|
|
432
|
+
this.config.onCredentialRotated?.({ reason: "unauthorized" });
|
|
433
|
+
finalOptions = {
|
|
434
|
+
...finalOptions,
|
|
435
|
+
headers: {
|
|
436
|
+
...finalOptions.headers,
|
|
437
|
+
Authorization: `${refreshed.scheme ?? "Bearer"} ${refreshed.token}`
|
|
438
|
+
}
|
|
439
|
+
};
|
|
440
|
+
response = await fetch(url, { ...finalOptions, signal: controller.signal });
|
|
441
|
+
}
|
|
442
|
+
}
|
|
387
443
|
clearTimeout(timeoutId);
|
|
388
444
|
if (this.rateLimitTracker) {
|
|
389
445
|
this.rateLimitTracker.updateFromHeaders(response.headers);
|
|
@@ -472,9 +528,11 @@ var BaseClient = class {
|
|
|
472
528
|
*/
|
|
473
529
|
async requestWithRetry(endpoint, options = {}, serviceURL, retries = this.config.retries, requestOptions) {
|
|
474
530
|
let lastError;
|
|
531
|
+
const idempotencyKey = this.resolveIdempotencyKey(options.method || "GET", requestOptions);
|
|
532
|
+
const attemptOptions = idempotencyKey ? { ...requestOptions, idempotencyKey } : requestOptions;
|
|
475
533
|
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
476
534
|
try {
|
|
477
|
-
return await this.request(endpoint, options, serviceURL,
|
|
535
|
+
return await this.request(endpoint, options, serviceURL, attemptOptions);
|
|
478
536
|
} catch (error) {
|
|
479
537
|
lastError = error instanceof Error ? error : new Error("Unknown error");
|
|
480
538
|
if (requestOptions?.signal?.aborted) {
|
|
@@ -571,6 +629,9 @@ var BaseClient = class {
|
|
|
571
629
|
if (config.interceptors) {
|
|
572
630
|
this.interceptors = config.interceptors;
|
|
573
631
|
}
|
|
632
|
+
if (config.authProvider !== void 0 || config.apiKey !== void 0) {
|
|
633
|
+
this.authProvider = this.config.authProvider ?? (this.config.apiKey ? new StaticApiKeyProvider(this.config.apiKey) : void 0);
|
|
634
|
+
}
|
|
574
635
|
}
|
|
575
636
|
/**
|
|
576
637
|
* Get current configuration (readonly)
|
|
@@ -598,27 +659,63 @@ var BaseClient = class {
|
|
|
598
659
|
}
|
|
599
660
|
};
|
|
600
661
|
|
|
601
|
-
// src/geolocation/
|
|
602
|
-
var
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
);
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
662
|
+
// src/geolocation/integrity.ts
|
|
663
|
+
var isNil = (v) => v === null || v === void 0;
|
|
664
|
+
function assessGpsIntegrity(input, opts = {}) {
|
|
665
|
+
const flags = [];
|
|
666
|
+
const now = opts.now ?? (() => Date.now());
|
|
667
|
+
const maxAgeMs = opts.maxAgeMs ?? 6e4;
|
|
668
|
+
const minAccuracy = opts.minAccuracyMeters ?? 1;
|
|
669
|
+
const { accuracy, altitude, altitudeAccuracy, heading, speed, timestamp } = input;
|
|
670
|
+
if (isNil(accuracy)) {
|
|
671
|
+
flags.push("accuracy_missing");
|
|
672
|
+
} else if (accuracy === 0) {
|
|
673
|
+
flags.push("accuracy_zero");
|
|
674
|
+
} else if (accuracy < minAccuracy) {
|
|
675
|
+
flags.push("accuracy_implausibly_small");
|
|
676
|
+
}
|
|
677
|
+
const hasMotion = !isNil(heading) || !isNil(speed);
|
|
678
|
+
if (hasMotion && (isNil(accuracy) || accuracy === 0)) {
|
|
679
|
+
flags.push("motion_without_accuracy");
|
|
680
|
+
}
|
|
681
|
+
if (!isNil(altitude) && isNil(altitudeAccuracy)) {
|
|
682
|
+
flags.push("altitude_without_accuracy");
|
|
683
|
+
}
|
|
684
|
+
if (timestamp !== void 0 && now() - timestamp > maxAgeMs) {
|
|
685
|
+
flags.push("stale_timestamp");
|
|
618
686
|
}
|
|
619
|
-
|
|
620
|
-
|
|
687
|
+
if (opts.permissionState === "denied" || opts.permissionState === "prompt") {
|
|
688
|
+
flags.push(`permission_${opts.permissionState}`);
|
|
689
|
+
}
|
|
690
|
+
return { suspicious: flags.length > 0, flags };
|
|
621
691
|
}
|
|
692
|
+
function gpsIntegrityInputFromPosition(position) {
|
|
693
|
+
const c = position.coords;
|
|
694
|
+
return {
|
|
695
|
+
latitude: c.latitude,
|
|
696
|
+
longitude: c.longitude,
|
|
697
|
+
accuracy: c.accuracy,
|
|
698
|
+
altitude: c.altitude,
|
|
699
|
+
altitudeAccuracy: c.altitudeAccuracy,
|
|
700
|
+
heading: c.heading,
|
|
701
|
+
speed: c.speed,
|
|
702
|
+
timestamp: position.timestamp
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
async function probeGeolocationPermission() {
|
|
706
|
+
try {
|
|
707
|
+
if (typeof navigator === "undefined" || !navigator.permissions?.query) {
|
|
708
|
+
return "unavailable";
|
|
709
|
+
}
|
|
710
|
+
const status = await navigator.permissions.query({ name: "geolocation" });
|
|
711
|
+
return status.state;
|
|
712
|
+
} catch {
|
|
713
|
+
return "unavailable";
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
// src/geolocation/ciphertext.ts
|
|
718
|
+
var CIPHER_TEXT_EXPIRY_MINUTES = 5;
|
|
622
719
|
function getWebGLInfo() {
|
|
623
720
|
if (typeof document === "undefined") return null;
|
|
624
721
|
try {
|
|
@@ -650,6 +747,7 @@ async function requestGPSLocation(timeout = 1e4, highAccuracy = true) {
|
|
|
650
747
|
if (typeof navigator === "undefined" || !navigator.geolocation) {
|
|
651
748
|
return null;
|
|
652
749
|
}
|
|
750
|
+
const permissionState = await probeGeolocationPermission();
|
|
653
751
|
return new Promise((resolve) => {
|
|
654
752
|
navigator.geolocation.getCurrentPosition(
|
|
655
753
|
(position) => {
|
|
@@ -666,6 +764,7 @@ async function requestGPSLocation(timeout = 1e4, highAccuracy = true) {
|
|
|
666
764
|
resolve(null);
|
|
667
765
|
return;
|
|
668
766
|
}
|
|
767
|
+
const { flags } = assessGpsIntegrity(gpsIntegrityInputFromPosition(position), { permissionState });
|
|
669
768
|
resolve({
|
|
670
769
|
latitude,
|
|
671
770
|
longitude,
|
|
@@ -674,7 +773,8 @@ async function requestGPSLocation(timeout = 1e4, highAccuracy = true) {
|
|
|
674
773
|
altitude_accuracy: position.coords.altitudeAccuracy ?? void 0,
|
|
675
774
|
heading: position.coords.heading ?? void 0,
|
|
676
775
|
speed: position.coords.speed ?? void 0,
|
|
677
|
-
timestamp: position.timestamp
|
|
776
|
+
timestamp: position.timestamp,
|
|
777
|
+
integrity_flags: flags.length ? flags : void 0
|
|
678
778
|
});
|
|
679
779
|
},
|
|
680
780
|
() => {
|
|
@@ -741,6 +841,9 @@ async function generateCipherText(options, config) {
|
|
|
741
841
|
);
|
|
742
842
|
if (location) {
|
|
743
843
|
locationData = location;
|
|
844
|
+
if (location.integrity_flags?.length) {
|
|
845
|
+
warnings.push(`GPS integrity flags (advisory): ${location.integrity_flags.join(", ")}`);
|
|
846
|
+
}
|
|
744
847
|
} else if (gpsRequiredByConfig) {
|
|
745
848
|
throw new VesantError(
|
|
746
849
|
`GPS location is required for ${options.reason} by tenant configuration, but GPS was not available or permission was denied`,
|
|
@@ -770,15 +873,7 @@ async function generateCipherText(options, config) {
|
|
|
770
873
|
};
|
|
771
874
|
const encoded = encodePayload(payload);
|
|
772
875
|
const timestamp = now.getTime().toString(36);
|
|
773
|
-
|
|
774
|
-
const hmacKey = options.signingKey || options.apiKey;
|
|
775
|
-
if (hmacKey) {
|
|
776
|
-
const message = `02.${timestamp}.${encoded}`;
|
|
777
|
-
const hmac = await computeHMAC(hmacKey, message);
|
|
778
|
-
cipherText = `${message}.${hmac}`;
|
|
779
|
-
} else {
|
|
780
|
-
cipherText = `01.${timestamp}.${encoded}`;
|
|
781
|
-
}
|
|
876
|
+
const cipherText = `01.${timestamp}.${encoded}`;
|
|
782
877
|
return {
|
|
783
878
|
cipherText,
|
|
784
879
|
locationCaptured: !!locationData,
|
|
@@ -787,6 +882,7 @@ async function generateCipherText(options, config) {
|
|
|
787
882
|
expiresAt: expiry.toISOString()
|
|
788
883
|
};
|
|
789
884
|
}
|
|
885
|
+
var collectDeviceSignals = generateCipherText;
|
|
790
886
|
function decodeCipherText(cipherText) {
|
|
791
887
|
try {
|
|
792
888
|
const parts = cipherText.split(".");
|
|
@@ -814,6 +910,7 @@ function isCipherTextExpired(cipherText) {
|
|
|
814
910
|
}
|
|
815
911
|
|
|
816
912
|
// src/geolocation/client.ts
|
|
913
|
+
var DEFAULT_GPS_CONFIG_TTL_MS = 5 * 60 * 1e3;
|
|
817
914
|
var GeolocationClient = class extends BaseClient {
|
|
818
915
|
constructor(config) {
|
|
819
916
|
const baseConfig = {
|
|
@@ -829,6 +926,8 @@ var GeolocationClient = class extends BaseClient {
|
|
|
829
926
|
environment: config.environment
|
|
830
927
|
};
|
|
831
928
|
super(baseConfig);
|
|
929
|
+
this.gpsConfigCache = null;
|
|
930
|
+
this.gpsConfigTtlMs = config.gpsConfigTtlMs ?? DEFAULT_GPS_CONFIG_TTL_MS;
|
|
832
931
|
}
|
|
833
932
|
// ============================================================================
|
|
834
933
|
// IP Verification & Compliance
|
|
@@ -860,8 +959,8 @@ var GeolocationClient = class extends BaseClient {
|
|
|
860
959
|
* ```
|
|
861
960
|
*/
|
|
862
961
|
async verifyIP(request, requestOptions) {
|
|
863
|
-
if (!request.ip_address
|
|
864
|
-
throw new ValidationError("ip_address
|
|
962
|
+
if (request.ip_address !== void 0 && !request.ip_address.trim()) {
|
|
963
|
+
throw new ValidationError("ip_address, when provided, must be a non-empty string", ["ip_address"]);
|
|
865
964
|
}
|
|
866
965
|
return this.requestWithRetry("/api/v1/geo/verify", {
|
|
867
966
|
method: "POST",
|
|
@@ -914,37 +1013,85 @@ var GeolocationClient = class extends BaseClient {
|
|
|
914
1013
|
* ```
|
|
915
1014
|
*/
|
|
916
1015
|
async getGPSConfig(requestOptions) {
|
|
917
|
-
const
|
|
918
|
-
if (
|
|
919
|
-
|
|
1016
|
+
const cached = this.gpsConfigCache;
|
|
1017
|
+
if (cached && cached.expiresAt > Date.now()) {
|
|
1018
|
+
return cached.value;
|
|
920
1019
|
}
|
|
921
|
-
return
|
|
1020
|
+
return this.refreshGPSConfig(requestOptions);
|
|
922
1021
|
}
|
|
923
1022
|
/**
|
|
924
|
-
*
|
|
1023
|
+
* Re-read the tenant's GPS requirement configuration, ignoring any copy held
|
|
1024
|
+
* from an earlier read.
|
|
925
1025
|
*
|
|
926
|
-
*
|
|
927
|
-
*
|
|
1026
|
+
* These requirements are edited in the compliance console while your
|
|
1027
|
+
* application is running. `getGPSConfig` reuses a fetched copy for a short
|
|
1028
|
+
* period so a long-lived session does not call the API on every event; call
|
|
1029
|
+
* this when you need a change to take effect immediately — for example right
|
|
1030
|
+
* after an administrator saves new settings.
|
|
928
1031
|
*
|
|
929
|
-
* @returns The
|
|
1032
|
+
* @returns The current GPS requirement config per event type
|
|
1033
|
+
*
|
|
1034
|
+
* @example
|
|
1035
|
+
* ```typescript
|
|
1036
|
+
* const config = await client.refreshGPSConfig();
|
|
1037
|
+
* if (config.require_gps.login) {
|
|
1038
|
+
* // GPS is now required for login
|
|
1039
|
+
* }
|
|
1040
|
+
* ```
|
|
1041
|
+
*/
|
|
1042
|
+
async refreshGPSConfig(requestOptions) {
|
|
1043
|
+
const value = await this.requestWithRetry(
|
|
1044
|
+
"/api/v1/geo/config",
|
|
1045
|
+
void 0,
|
|
1046
|
+
void 0,
|
|
1047
|
+
void 0,
|
|
1048
|
+
requestOptions
|
|
1049
|
+
);
|
|
1050
|
+
this.gpsConfigCache = { value, expiresAt: Date.now() + this.gpsConfigTtlMs };
|
|
1051
|
+
return value;
|
|
1052
|
+
}
|
|
1053
|
+
/**
|
|
1054
|
+
* Mint a short-lived, single-use device-signals nonce.
|
|
1055
|
+
*
|
|
1056
|
+
* The nonce is bound to the tenant, user, and event type and is consumed
|
|
1057
|
+
* server-side during validation, so a captured device-signals envelope cannot
|
|
1058
|
+
* be replayed. `validateCipherText` mints and attaches one automatically; call
|
|
1059
|
+
* this directly only if you manage the nonce yourself.
|
|
1060
|
+
*
|
|
1061
|
+
* @param userId - User ID the nonce is bound to
|
|
1062
|
+
* @param eventType - Event type the nonce is bound to (login, registration, ...)
|
|
1063
|
+
* @returns The nonce and its expiry
|
|
1064
|
+
*/
|
|
1065
|
+
async fetchSignalsNonce(userId, eventType, requestOptions) {
|
|
1066
|
+
if (!userId?.trim()) {
|
|
1067
|
+
throw new ValidationError("userId is required and must be a non-empty string", ["userId"]);
|
|
1068
|
+
}
|
|
1069
|
+
const request = { user_id: userId, event_type: eventType };
|
|
1070
|
+
return this.request(
|
|
1071
|
+
"/api/v1/geo/signals-token",
|
|
1072
|
+
{
|
|
1073
|
+
method: "POST",
|
|
1074
|
+
body: JSON.stringify(request)
|
|
1075
|
+
},
|
|
1076
|
+
void 0,
|
|
1077
|
+
requestOptions
|
|
1078
|
+
);
|
|
1079
|
+
}
|
|
1080
|
+
/**
|
|
1081
|
+
* Best-effort nonce mint for validation. Returns undefined if minting fails —
|
|
1082
|
+
* the server is the sole authority and blocks when a nonce is required, so this
|
|
1083
|
+
* never downgrades security on the client's say-so.
|
|
930
1084
|
*/
|
|
931
|
-
async
|
|
1085
|
+
async resolveSignalsNonce(userId, eventType, requestOptions) {
|
|
932
1086
|
try {
|
|
933
|
-
const
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
requestOptions
|
|
1087
|
+
const { nonce } = await this.fetchSignalsNonce(userId, eventType, requestOptions);
|
|
1088
|
+
return nonce;
|
|
1089
|
+
} catch (err) {
|
|
1090
|
+
this.logger.warn(
|
|
1091
|
+
`Failed to mint device-signals nonce; proceeding without it: ${err instanceof Error ? err.message : String(err)}`
|
|
939
1092
|
);
|
|
940
|
-
|
|
941
|
-
this.cachedSigningKey = response.signing_key;
|
|
942
|
-
return response.signing_key;
|
|
943
|
-
}
|
|
944
|
-
} catch {
|
|
1093
|
+
return void 0;
|
|
945
1094
|
}
|
|
946
|
-
const config = await this.getGPSConfig(requestOptions);
|
|
947
|
-
return config.signing_key;
|
|
948
1095
|
}
|
|
949
1096
|
// ============================================================================
|
|
950
1097
|
// CipherText Validation
|
|
@@ -997,12 +1144,14 @@ var GeolocationClient = class extends BaseClient {
|
|
|
997
1144
|
if (!userId?.trim()) {
|
|
998
1145
|
throw new ValidationError("userId is required and must be a non-empty string", ["userId"]);
|
|
999
1146
|
}
|
|
1147
|
+
const nonce = await this.resolveSignalsNonce(userId, eventType, requestOptions);
|
|
1000
1148
|
const request = {
|
|
1001
1149
|
cipher_text: cipherText,
|
|
1002
1150
|
user_id: userId,
|
|
1003
1151
|
event_type: eventType,
|
|
1004
1152
|
expected_ip: expectedIP,
|
|
1005
|
-
customer_data: customerData
|
|
1153
|
+
customer_data: customerData,
|
|
1154
|
+
nonce
|
|
1006
1155
|
};
|
|
1007
1156
|
return this.requestWithRetry(
|
|
1008
1157
|
"/api/v1/geo/validate-ciphertext",
|
|
@@ -1211,20 +1360,12 @@ var GeolocationClient = class extends BaseClient {
|
|
|
1211
1360
|
*
|
|
1212
1361
|
* @example
|
|
1213
1362
|
* ```typescript
|
|
1214
|
-
* //
|
|
1363
|
+
* // GPS (the web SDK is GPS + IP only; the server handles the IP fallback)
|
|
1215
1364
|
* const result = await client.captureLocation(token, {
|
|
1216
1365
|
* latitude: 37.7749,
|
|
1217
1366
|
* longitude: -122.4194,
|
|
1218
1367
|
* accuracy: 10
|
|
1219
1368
|
* });
|
|
1220
|
-
*
|
|
1221
|
-
* // Using WiFi positioning (fallback)
|
|
1222
|
-
* const result = await client.captureLocation(token, {
|
|
1223
|
-
* wifi_networks: [
|
|
1224
|
-
* { macAddress: "00:11:22:33:44:55", signalStrength: -50 },
|
|
1225
|
-
* { macAddress: "AA:BB:CC:DD:EE:FF", signalStrength: -70 }
|
|
1226
|
-
* ]
|
|
1227
|
-
* });
|
|
1228
1369
|
* ```
|
|
1229
1370
|
*/
|
|
1230
1371
|
async captureLocation(token, capture, requestOptions) {
|
|
@@ -1237,40 +1378,28 @@ var GeolocationClient = class extends BaseClient {
|
|
|
1237
1378
|
// CipherText Generation
|
|
1238
1379
|
// ============================================================================
|
|
1239
1380
|
/**
|
|
1240
|
-
* Generate a
|
|
1381
|
+
* Generate a device-signals envelope containing device and location data,
|
|
1382
|
+
* validated by the server.
|
|
1241
1383
|
*
|
|
1242
|
-
*
|
|
1243
|
-
* If no API key is configured, falls back to unsigned v01 format.
|
|
1244
|
-
*
|
|
1245
|
-
* @param options - Options for cipherText generation
|
|
1384
|
+
* @param options - Options for envelope generation
|
|
1246
1385
|
* @param gpsConfig - Optional GPS config (from getGPSConfig)
|
|
1247
|
-
* @returns
|
|
1386
|
+
* @returns The device-signals envelope result
|
|
1248
1387
|
*
|
|
1249
1388
|
* @example
|
|
1250
1389
|
* ```typescript
|
|
1251
1390
|
* const result = await client.generateCipherText({ reason: 'login' });
|
|
1252
|
-
* console.log(result.cipherText); //
|
|
1391
|
+
* console.log(result.cipherText); // device-signals envelope
|
|
1253
1392
|
* ```
|
|
1254
1393
|
*/
|
|
1255
1394
|
async generateCipherText(options, gpsConfig) {
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
if (!signingKey) {
|
|
1259
|
-
signingKey = await this.fetchSigningKey();
|
|
1260
|
-
}
|
|
1261
|
-
if (!resolvedGpsConfig) {
|
|
1262
|
-
resolvedGpsConfig = await this.getGPSConfig();
|
|
1263
|
-
}
|
|
1264
|
-
return generateCipherText(
|
|
1265
|
-
{ ...options, signingKey: signingKey || void 0 },
|
|
1266
|
-
resolvedGpsConfig
|
|
1267
|
-
);
|
|
1395
|
+
const resolvedGpsConfig = gpsConfig ?? await this.getGPSConfig();
|
|
1396
|
+
return generateCipherText(options, resolvedGpsConfig);
|
|
1268
1397
|
}
|
|
1269
1398
|
// ============================================================================
|
|
1270
1399
|
// Utility Methods (inherited from BaseClient: healthCheck, updateConfig, getConfig, buildQueryString)
|
|
1271
1400
|
// ============================================================================
|
|
1272
1401
|
};
|
|
1273
1402
|
|
|
1274
|
-
export { GeolocationClient, decodeCipherText, generateCipherText, isCipherTextExpired };
|
|
1403
|
+
export { GeolocationClient, assessGpsIntegrity, collectDeviceSignals, decodeCipherText, generateCipherText, gpsIntegrityInputFromPosition, isCipherTextExpired, probeGeolocationPermission };
|
|
1275
1404
|
//# sourceMappingURL=index.mjs.map
|
|
1276
1405
|
//# sourceMappingURL=index.mjs.map
|