@safepassage/sdk 3.5.5 → 3.5.6

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/index.js CHANGED
@@ -1,7 +1,6 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __defProps = Object.defineProperties;
3
3
  var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
4
  var __getOwnPropSymbols = Object.getOwnPropertySymbols;
6
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
7
6
  var __propIsEnum = Object.prototype.propertyIsEnumerable;
@@ -18,30 +17,43 @@ var __spreadValues = (a, b) => {
18
17
  return a;
19
18
  };
20
19
  var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
21
- var __objRest = (source, exclude) => {
22
- var target = {};
23
- for (var prop in source)
24
- if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
25
- target[prop] = source[prop];
26
- if (source != null && __getOwnPropSymbols)
27
- for (var prop of __getOwnPropSymbols(source)) {
28
- if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
29
- target[prop] = source[prop];
30
- }
31
- return target;
32
- };
33
- var __esm = (fn, res, err) => function __init() {
34
- if (err) throw err[0];
35
- try {
36
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
37
- } catch (e) {
38
- throw err = [e], e;
20
+
21
+ // src-redirect/utils/polyfills.ts
22
+ function setupPolyfills() {
23
+ if (!crypto.randomUUID) {
24
+ crypto.randomUUID = function() {
25
+ const array = new Uint8Array(16);
26
+ crypto.getRandomValues(array);
27
+ array[6] = array[6] & 15 | 64;
28
+ array[8] = array[8] & 63 | 128;
29
+ const hex = Array.from(array).map((b) => b.toString(16).padStart(2, "0")).join("");
30
+ return [
31
+ hex.slice(0, 8),
32
+ hex.slice(8, 12),
33
+ hex.slice(12, 16),
34
+ hex.slice(16, 20),
35
+ hex.slice(20, 32)
36
+ ].join("-");
37
+ };
39
38
  }
40
- };
41
- var __export = (target, all) => {
42
- for (var name in all)
43
- __defProp(target, name, { get: all[name], enumerable: true });
44
- };
39
+ }
40
+ function checkBrowserCompatibility(logLabel = "SDK") {
41
+ const warnings = [];
42
+ if (!window.crypto || !window.crypto.getRandomValues) {
43
+ throw new Error(`${logLabel} requires Web Crypto API support`);
44
+ }
45
+ if (!crypto.randomUUID) {
46
+ warnings.push("crypto.randomUUID not supported, using polyfill");
47
+ }
48
+ if (!window.URLSearchParams) {
49
+ warnings.push(
50
+ "URLSearchParams not supported, consider adding a polyfill for IE 11 support"
51
+ );
52
+ }
53
+ if (warnings.length > 0) {
54
+ console.warn(`${logLabel} Browser Compatibility:`, warnings.join("; "));
55
+ }
56
+ }
45
57
 
46
58
  // src-redirect/utils/security.ts
47
59
  function isOriginTrusted(origin, trustedOrigins) {
@@ -135,6 +147,34 @@ function validateReturnUrl(url, environment, _logLabel = "SDK") {
135
147
  return { isValid: false, error: "Invalid URL format" };
136
148
  }
137
149
  }
150
+ var VerificationRateLimit = class {
151
+ constructor() {
152
+ this.attempts = /* @__PURE__ */ new Map();
153
+ this.maxAttempts = 5;
154
+ this.timeWindow = 6e4;
155
+ }
156
+ // 1 minute
157
+ isAllowed(identifier, logLabel = "SDK") {
158
+ const now = Date.now();
159
+ const attempts = this.attempts.get(identifier) || [];
160
+ const recentAttempts = attempts.filter(
161
+ (time) => now - time < this.timeWindow
162
+ );
163
+ if (recentAttempts.length >= this.maxAttempts) {
164
+ console.warn(
165
+ `${logLabel} Security: Rate limit exceeded for ${identifier}`
166
+ );
167
+ return false;
168
+ }
169
+ recentAttempts.push(now);
170
+ this.attempts.set(identifier, recentAttempts);
171
+ return true;
172
+ }
173
+ reset(identifier) {
174
+ this.attempts.delete(identifier);
175
+ }
176
+ };
177
+ var verificationRateLimit = new VerificationRateLimit();
138
178
  function logSecurityEvent(event, metadata, logLabel = "SDK") {
139
179
  const context = {
140
180
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
@@ -143,139 +183,13 @@ function logSecurityEvent(event, metadata, logLabel = "SDK") {
143
183
  };
144
184
  console.warn(`${logLabel} Security Event: ${event}`, __spreadValues(__spreadValues({}, context), metadata));
145
185
  }
146
- var VerificationRateLimit, verificationRateLimit;
147
- var init_security = __esm({
148
- "src-redirect/utils/security.ts"() {
149
- "use strict";
150
- VerificationRateLimit = class {
151
- constructor() {
152
- this.attempts = /* @__PURE__ */ new Map();
153
- this.maxAttempts = 5;
154
- this.timeWindow = 6e4;
155
- }
156
- // 1 minute
157
- isAllowed(identifier, logLabel = "SDK") {
158
- const now = Date.now();
159
- const attempts = this.attempts.get(identifier) || [];
160
- const recentAttempts = attempts.filter(
161
- (time) => now - time < this.timeWindow
162
- );
163
- if (recentAttempts.length >= this.maxAttempts) {
164
- console.warn(
165
- `${logLabel} Security: Rate limit exceeded for ${identifier}`
166
- );
167
- return false;
168
- }
169
- recentAttempts.push(now);
170
- this.attempts.set(identifier, recentAttempts);
171
- return true;
172
- }
173
- reset(identifier) {
174
- this.attempts.delete(identifier);
175
- }
176
- };
177
- verificationRateLimit = new VerificationRateLimit();
178
- }
179
- });
180
-
181
- // src-redirect/utils/crypto.ts
182
- var crypto_exports = {};
183
- __export(crypto_exports, {
184
- createSignedState: () => createSignedState,
185
- generateHMAC: () => generateHMAC,
186
- generateSecureToken: () => generateSecureToken,
187
- parseSignedState: () => parseSignedState,
188
- verifyHMAC: () => verifyHMAC
189
- });
190
- async function generateHMAC(data, secret) {
191
- const encoder = new TextEncoder();
192
- const keyData = encoder.encode(secret);
193
- const dataBuffer = encoder.encode(data);
194
- const key = await crypto.subtle.importKey(
195
- "raw",
196
- keyData,
197
- { name: "HMAC", hash: "SHA-256" },
198
- false,
199
- ["sign"]
200
- );
201
- const signature = await crypto.subtle.sign("HMAC", key, dataBuffer);
202
- return Array.from(new Uint8Array(signature)).map((b) => b.toString(16).padStart(2, "0")).join("");
203
- }
204
- async function verifyHMAC(data, signature, secret) {
205
- try {
206
- const expectedSignature = await generateHMAC(data, secret);
207
- return constantTimeCompare(signature, expectedSignature);
208
- } catch (e) {
209
- return false;
210
- }
211
- }
212
- function constantTimeCompare(a, b) {
213
- if (a.length !== b.length) {
214
- return false;
215
- }
216
- let result = 0;
217
- for (let i = 0; i < a.length; i++) {
218
- result |= a.charCodeAt(i) ^ b.charCodeAt(i);
219
- }
220
- return result === 0;
221
- }
222
- function generateSecureToken(length = 32) {
223
- const array = new Uint8Array(length);
224
- crypto.getRandomValues(array);
225
- return Array.from(array, (b) => b.toString(16).padStart(2, "0")).join("");
226
- }
227
- async function createSignedState(payload, hmacSecret) {
228
- const timestampedPayload = __spreadProps(__spreadValues({}, payload), {
229
- timestamp: Date.now(),
230
- nonce: generateSecureToken(16)
231
- });
232
- const dataString = JSON.stringify(timestampedPayload);
233
- const signature = await generateHMAC(dataString, hmacSecret);
234
- const signedPayload = {
235
- data: timestampedPayload,
236
- signature
237
- };
238
- return btoa(JSON.stringify(signedPayload));
239
- }
240
- async function parseSignedState(signedState, hmacSecret, maxAge = STATE_EXPIRY_MS, logLabel = "SDK") {
241
- try {
242
- const json = atob(signedState);
243
- const signedPayload = JSON.parse(json);
244
- if (!signedPayload.data || !signedPayload.signature) {
245
- console.warn(`${logLabel}: Invalid signed state format`);
246
- return null;
247
- }
248
- const { data, signature } = signedPayload;
249
- const dataString = JSON.stringify(data);
250
- const isValid = await verifyHMAC(dataString, signature, hmacSecret);
251
- if (!isValid) {
252
- console.warn(`${logLabel}: State signature verification failed`);
253
- return null;
254
- }
255
- if (data.timestamp) {
256
- const age = Date.now() - data.timestamp;
257
- if (age > maxAge) {
258
- console.warn(`${logLabel}: State parameter expired`, { age, maxAge });
259
- return null;
260
- }
261
- }
262
- const _a = data, { timestamp, nonce } = _a, payload = __objRest(_a, ["timestamp", "nonce"]);
263
- void timestamp;
264
- void nonce;
265
- return payload;
266
- } catch (error) {
267
- console.warn(`${logLabel}: Failed to parse signed state`, error);
268
- return null;
269
- }
270
- }
271
- var init_crypto = __esm({
272
- "src-redirect/utils/crypto.ts"() {
273
- "use strict";
274
- init_validation();
275
- }
276
- });
277
186
 
278
187
  // src-redirect/utils/validation.ts
188
+ var MINIMUM_AGE = 25;
189
+ var MAXIMUM_AGE = 150;
190
+ var MAX_URL_LENGTH = 2048;
191
+ var MAX_API_KEY_LENGTH = 128;
192
+ var API_KEY_PATTERN = /^(pk_|sk_)[a-zA-Z0-9_]+$/;
279
193
  function validateConfig(config, context) {
280
194
  var _a;
281
195
  if (!config.apiKey) {
@@ -345,68 +259,6 @@ function validateConfig(config, context) {
345
259
  throw new Error("newTabTarget must be popup or tab");
346
260
  }
347
261
  }
348
- async function generateState(payload, environment, hmacSecret, _logLabel = "SDK") {
349
- const { createSignedState: createSignedState2 } = await Promise.resolve().then(() => (init_crypto(), crypto_exports));
350
- return createSignedState2(payload, hmacSecret);
351
- }
352
- var MINIMUM_AGE, MAXIMUM_AGE, MAX_URL_LENGTH, MAX_API_KEY_LENGTH, STATE_EXPIRY_MS, API_KEY_PATTERN;
353
- var init_validation = __esm({
354
- "src-redirect/utils/validation.ts"() {
355
- "use strict";
356
- init_security();
357
- MINIMUM_AGE = 25;
358
- MAXIMUM_AGE = 150;
359
- MAX_URL_LENGTH = 2048;
360
- MAX_API_KEY_LENGTH = 128;
361
- STATE_EXPIRY_MS = 6e5;
362
- API_KEY_PATTERN = /^(pk_|sk_)[a-zA-Z0-9_]+$/;
363
- }
364
- });
365
-
366
- // src-redirect/utils/polyfills.ts
367
- function setupPolyfills() {
368
- if (!crypto.randomUUID) {
369
- crypto.randomUUID = function() {
370
- const array = new Uint8Array(16);
371
- crypto.getRandomValues(array);
372
- array[6] = array[6] & 15 | 64;
373
- array[8] = array[8] & 63 | 128;
374
- const hex = Array.from(array).map((b) => b.toString(16).padStart(2, "0")).join("");
375
- return [
376
- hex.slice(0, 8),
377
- hex.slice(8, 12),
378
- hex.slice(12, 16),
379
- hex.slice(16, 20),
380
- hex.slice(20, 32)
381
- ].join("-");
382
- };
383
- }
384
- }
385
- function checkBrowserCompatibility(logLabel = "SDK") {
386
- const warnings = [];
387
- if (!window.crypto || !window.crypto.getRandomValues) {
388
- throw new Error(`${logLabel} requires Web Crypto API support`);
389
- }
390
- if (!window.crypto.subtle) {
391
- throw new Error(
392
- `${logLabel} requires Web Crypto subtle API for HMAC operations`
393
- );
394
- }
395
- if (!crypto.randomUUID) {
396
- warnings.push("crypto.randomUUID not supported, using polyfill");
397
- }
398
- if (!window.URLSearchParams) {
399
- warnings.push(
400
- "URLSearchParams not supported, consider adding a polyfill for IE 11 support"
401
- );
402
- }
403
- if (warnings.length > 0) {
404
- console.warn(`${logLabel} Browser Compatibility:`, warnings.join("; "));
405
- }
406
- }
407
-
408
- // src-redirect/core/VerificationSDK.ts
409
- init_validation();
410
262
 
411
263
  // src-redirect/utils/environment.ts
412
264
  function getEnvironmentUrl(environment, urls) {
@@ -525,7 +377,6 @@ function validateEnvironmentSecurity(environment, urls, logLabel = "SDK") {
525
377
  }
526
378
 
527
379
  // src-redirect/core/VerificationSDK.ts
528
- init_security();
529
380
  var _VerificationSDK = class _VerificationSDK {
530
381
  /**
531
382
  * Initialize SDK
@@ -542,16 +393,10 @@ var _VerificationSDK = class _VerificationSDK {
542
393
  this.isVerificationInProgress = false;
543
394
  this.currentSessionId = null;
544
395
  this.hasReceivedResult = false;
545
- // Server-provided verify URL (includes sessionToken)
396
+ // Server-provided verify URL (includes authoritative session context)
546
397
  this.lastVerifyUrl = null;
547
- // Server-provided session token (WS auth)
548
- this.lastSessionToken = null;
549
398
  // External user ID provided during verify() for cancellation redirects
550
399
  this.lastExternalUserId = null;
551
- // Sandbox mode flag from session creation (for UI labeling)
552
- this.lastSandboxMode = null;
553
- // Temporary storage for QR handoff token to include in state
554
- this.temporaryHandoffToken = null;
555
400
  this.brandUrls = brandUrls;
556
401
  this.brandConstants = brandConstants;
557
402
  const environment = resolveEnvironment(config.environment, this.brandUrls);
@@ -570,6 +415,7 @@ var _VerificationSDK = class _VerificationSDK {
570
415
  mode: config.mode || "redirect",
571
416
  newTabTarget: config.newTabTarget || "popup"
572
417
  });
418
+ this.validateServiceOverrides();
573
419
  validateEnvironmentSecurity(this.config.environment, this.getUrlConfig(), this.brandConstants.name);
574
420
  enforceHTTPS(this.config.environment, this.brandConstants.name);
575
421
  logSecurityEvent("SDK_INITIALIZED", {
@@ -648,82 +494,32 @@ var _VerificationSDK = class _VerificationSDK {
648
494
  throw error;
649
495
  }
650
496
  }
651
- /**
652
- * Build verification URL with HMAC-signed state
653
- */
497
+ /** Build a launch URL from the Portal-issued authoritative verify URL. */
654
498
  async buildVerificationUrl(options, sessionId) {
655
- var _a, _b;
656
- const baseUrl = this.config.verifyUrl || getEnvironmentUrl(this.config.environment, this.getUrlConfig());
657
- const hasExplicitChallengeAge = options.challengeAge !== void 0;
658
- const hasExplicitVerificationMode = options.verificationMode !== void 0;
659
- const hasExplicitFaceMatchEnabled = options.faceMatchEnabled !== void 0 || this.config.faceMatchEnabled !== void 0;
660
- const hasOverrides = hasExplicitChallengeAge || hasExplicitVerificationMode || hasExplicitFaceMatchEnabled;
661
- const faceMatchEnabled = (_a = options.faceMatchEnabled) != null ? _a : this.config.faceMatchEnabled;
662
- const state = await generateState(
663
- {
664
- merchantId: this.config.apiKey,
665
- sessionId,
666
- returnUrl: this.config.returnUrl,
667
- cancelUrl: this.config.cancelUrl,
668
- challengeAge: options.challengeAge || this.config.defaultChallengeAge,
669
- verificationMode: options.verificationMode || this.config.defaultVerificationMode,
670
- faceMatchEnabled,
671
- hasOverrides,
672
- // Flag to indicate explicit overrides
673
- externalUserId: options.externalUserId,
674
- timestamp: Date.now(),
675
- // Additional config for self-contained verify-ui
676
- apiUrl: this.getPortalApiUrl(),
677
- engineUrl: this.getEngineUrl(),
678
- wsUrl: this.getWebSocketUrl(),
679
- environment: this.config.environment,
680
- features: {
681
- testMode: false,
682
- warmupPeriodMs: 500,
683
- qualityThreshold: 0.6,
684
- sandboxMode: (_b = this.lastSandboxMode) != null ? _b : false
685
- },
686
- // Include handoffToken if available (for QR code desktop flow)
687
- handoffToken: this.temporaryHandoffToken || void 0,
688
- // Include sessionToken and verifyUrl to make UI auth deterministic
689
- sessionToken: this.lastSessionToken || void 0,
690
- verifyUrl: this.lastVerifyUrl || void 0
691
- },
692
- this.config.environment,
693
- this.getHmacSecret(),
694
- this.brandConstants.name
695
- );
499
+ if (!this.lastVerifyUrl) {
500
+ throw new Error("Server did not return an authoritative verification URL");
501
+ }
696
502
  const language = options.language || this.config.language;
697
- if (this.lastVerifyUrl) {
698
- try {
699
- const resolvedVerifyUrl = this.applyLocalVerifyOverride(this.lastVerifyUrl);
700
- const url = new URL(resolvedVerifyUrl);
701
- url.searchParams.set("state", state);
702
- url.searchParams.set("mode", this.config.mode);
703
- if (options.skipIntro) {
704
- url.searchParams.set("skip_intro", "true");
705
- }
706
- if (options.autoReturn) {
707
- url.searchParams.set("auto_return", "true");
708
- }
709
- if (language) {
710
- url.searchParams.set("lang", language);
711
- }
712
- return url.toString();
713
- } catch (e) {
714
- }
503
+ const serverUrl = new URL(this.lastVerifyUrl);
504
+ const trustedVerifyOrigin = new URL(this.getUrlConfig().verifyUiUrl).origin;
505
+ if (serverUrl.origin !== trustedVerifyOrigin) {
506
+ throw new Error("Server returned an untrusted verification origin");
715
507
  }
716
- const params = new URLSearchParams({ state, sessionId, mode: this.config.mode });
508
+ if (serverUrl.searchParams.get("sessionId") !== sessionId || !serverUrl.searchParams.get("sessionToken")) {
509
+ throw new Error("Server verification URL is missing authoritative session context");
510
+ }
511
+ const url = new URL(this.applyLocalVerifyOverride(serverUrl.toString()));
512
+ url.searchParams.set("mode", this.config.mode);
717
513
  if (options.skipIntro) {
718
- params.set("skip_intro", "true");
514
+ url.searchParams.set("skip_intro", "true");
719
515
  }
720
516
  if (options.autoReturn) {
721
- params.set("auto_return", "true");
517
+ url.searchParams.set("auto_return", "true");
722
518
  }
723
519
  if (language) {
724
- params.set("lang", language);
520
+ url.searchParams.set("lang", language);
725
521
  }
726
- return `${baseUrl}/?${params.toString()}`;
522
+ return url.toString();
727
523
  }
728
524
  /**
729
525
  * Redirect in same tab
@@ -981,22 +777,33 @@ var _VerificationSDK = class _VerificationSDK {
981
777
  * Get Portal API URL based on environment and brand
982
778
  */
983
779
  getPortalApiUrl() {
984
- if (this.config.apiUrl) {
780
+ const trustedApiUrl = this.getUrlConfig().apiUrl;
781
+ if (!this.config.apiUrl) {
782
+ return trustedApiUrl;
783
+ }
784
+ const configuredOrigin = this.getUrlOrigin(this.config.apiUrl);
785
+ const trustedOrigin = this.getUrlOrigin(trustedApiUrl);
786
+ if (configuredOrigin === trustedOrigin || this.getLocalOrigin(this.config.apiUrl)) {
985
787
  return this.config.apiUrl;
986
788
  }
987
- return this.getUrlConfig().apiUrl;
789
+ throw new Error("apiUrl must use the selected brand/environment service");
988
790
  }
989
- /**
990
- * Get Engine URL based on environment and brand
991
- */
992
- getEngineUrl() {
993
- return this.getUrlConfig().engineUrl;
994
- }
995
- /**
996
- * Get WebSocket URL based on environment and brand
997
- */
998
- getWebSocketUrl() {
999
- return this.getUrlConfig().wsUrl;
791
+ validateServiceOverrides() {
792
+ const trusted = this.getUrlConfig();
793
+ if (this.config.apiUrl) {
794
+ const configuredOrigin = this.getUrlOrigin(this.config.apiUrl);
795
+ const trustedOrigin = this.getUrlOrigin(trusted.apiUrl);
796
+ if (configuredOrigin !== trustedOrigin && !this.getLocalOrigin(this.config.apiUrl)) {
797
+ throw new Error("apiUrl must use the selected brand/environment service");
798
+ }
799
+ }
800
+ if (this.config.verifyUrl) {
801
+ const configuredOrigin = this.getUrlOrigin(this.config.verifyUrl);
802
+ const trustedOrigin = this.getUrlOrigin(trusted.verifyUiUrl);
803
+ if (configuredOrigin !== trustedOrigin && !this.getLocalOrigin(this.config.verifyUrl)) {
804
+ throw new Error("verifyUrl must use the selected brand/environment service");
805
+ }
806
+ }
1000
807
  }
1001
808
  /**
1002
809
  * Detect if this is a public key (pk_ prefix) vs private key (sk_ prefix)
@@ -1008,7 +815,7 @@ var _VerificationSDK = class _VerificationSDK {
1008
815
  * Create session internally for public keys
1009
816
  */
1010
817
  async createInternalSession(options) {
1011
- var _a, _b, _c;
818
+ var _a, _b;
1012
819
  try {
1013
820
  const portalApiUrl = this.getPortalApiUrl();
1014
821
  const response = await fetch(`${portalApiUrl}/api/v1/sessions/create`, {
@@ -1021,9 +828,6 @@ var _VerificationSDK = class _VerificationSDK {
1021
828
  merchantId: this.config.apiKey,
1022
829
  returnUrl: this.config.returnUrl,
1023
830
  cancelUrl: this.config.cancelUrl,
1024
- challengeAge: options.challengeAge,
1025
- verificationMode: options.verificationMode,
1026
- faceMatchEnabled: (_a = options.faceMatchEnabled) != null ? _a : this.config.faceMatchEnabled,
1027
831
  merchantName: document.title || window.location.hostname,
1028
832
  externalUserId: options.externalUserId
1029
833
  })
@@ -1033,7 +837,7 @@ var _VerificationSDK = class _VerificationSDK {
1033
837
  const errorCode = errorData == null ? void 0 : errorData.code;
1034
838
  if (this.isBillingBlockError(errorCode)) {
1035
839
  const language = options.language || this.config.language;
1036
- this.openBillingBlockPage(errorCode, errorData == null ? void 0 : errorData.portalUrl, language);
840
+ this.openBillingBlockPage(errorCode, language);
1037
841
  }
1038
842
  throw new Error(
1039
843
  `Failed to create session: ${response.status} ${response.statusText}. ${errorData.message || ""}`
@@ -1047,17 +851,6 @@ var _VerificationSDK = class _VerificationSDK {
1047
851
  if (sessionData.verifyUrl) {
1048
852
  this.lastVerifyUrl = sessionData.verifyUrl;
1049
853
  }
1050
- if (sessionData.sessionToken) {
1051
- this.lastSessionToken = sessionData.sessionToken;
1052
- }
1053
- if (sessionData.handoffToken) {
1054
- this.temporaryHandoffToken = sessionData.handoffToken;
1055
- }
1056
- if (typeof sessionData.sandboxMode === "boolean") {
1057
- this.lastSandboxMode = sessionData.sandboxMode;
1058
- } else {
1059
- this.lastSandboxMode = null;
1060
- }
1061
854
  logSecurityEvent("INTERNAL_SESSION_CREATED", {
1062
855
  sessionId: sessionId.substring(0, 8) + "...",
1063
856
  environment: this.config.environment,
@@ -1071,23 +864,20 @@ var _VerificationSDK = class _VerificationSDK {
1071
864
  environment: this.config.environment,
1072
865
  apiKeyType: "public"
1073
866
  }, this.brandConstants.name);
1074
- (_c = (_b = this.config).onError) == null ? void 0 : _c.call(_b, error);
867
+ (_b = (_a = this.config).onError) == null ? void 0 : _b.call(_a, error);
1075
868
  throw new Error(`Failed to create verification session: ${errorMessage}`);
1076
869
  }
1077
870
  }
1078
871
  isBillingBlockError(code) {
1079
872
  return code === "SUBSCRIPTION_REQUIRED" || code === "PLAN_LIMIT_REACHED" || code === "SANDBOX_LIMIT_REACHED";
1080
873
  }
1081
- openBillingBlockPage(code, portalUrl, language) {
874
+ openBillingBlockPage(code, language) {
1082
875
  var _a, _b, _c, _d;
1083
876
  try {
1084
- const baseUrl = this.config.verifyUrl || getEnvironmentUrl(this.config.environment, this.getUrlConfig());
877
+ const baseUrl = getEnvironmentUrl(this.config.environment, this.getUrlConfig());
1085
878
  const resolvedUrl = this.applyLocalVerifyOverride(baseUrl);
1086
879
  const url = new URL(resolvedUrl);
1087
880
  url.searchParams.set("blocked", code);
1088
- if (portalUrl) {
1089
- url.searchParams.set("portalUrl", portalUrl);
1090
- }
1091
881
  const effectiveLanguage = language || this.config.language;
1092
882
  if (effectiveLanguage) {
1093
883
  url.searchParams.set("lang", effectiveLanguage);
@@ -1150,6 +940,13 @@ var _VerificationSDK = class _VerificationSDK {
1150
940
  }
1151
941
  return null;
1152
942
  }
943
+ getUrlOrigin(urlValue) {
944
+ try {
945
+ return new URL(urlValue).origin;
946
+ } catch (e) {
947
+ return null;
948
+ }
949
+ }
1153
950
  applyLocalVerifyOverride(rawUrl) {
1154
951
  const overrideOrigin = this.getLocalOrigin(this.config.verifyUrl || null);
1155
952
  if (!overrideOrigin) {
@@ -1165,9 +962,6 @@ var _VerificationSDK = class _VerificationSDK {
1165
962
  return rawUrl;
1166
963
  }
1167
964
  }
1168
- getHmacSecret() {
1169
- return this.config.environment === "staging" ? this.brandConstants.hmacSecretStaging : this.brandConstants.hmacSecretProd;
1170
- }
1171
965
  };
1172
966
  // Local override hostnames allowed for internal testing
1173
967
  _VerificationSDK.LOCAL_HOSTNAMES = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "::1"]);
@@ -1180,28 +974,18 @@ var BRAND_URLS = {
1180
974
  verifyUiUrl: "https://av.safepassageapp.com",
1181
975
  engineUrl: "https://engine.safepassageapp.com",
1182
976
  wsUrl: "wss://engine.safepassageapp.com/api/websocket/stream",
1183
- trustedOrigins: [
1184
- "https://av.safepassageapp.com",
1185
- "https://portal.safepassageapp.com",
1186
- "https://api.safepassageapp.com"
1187
- ]
977
+ trustedOrigins: ["https://av.safepassageapp.com"]
1188
978
  },
1189
979
  staging: {
1190
980
  apiUrl: "https://api.staging.safepassageapp.com",
1191
981
  verifyUiUrl: "https://verify.staging.safepassageapp.com",
1192
982
  engineUrl: "https://engine.staging.safepassageapp.com",
1193
983
  wsUrl: "wss://engine.staging.safepassageapp.com/api/websocket/stream",
1194
- trustedOrigins: [
1195
- "https://verify.staging.safepassageapp.com",
1196
- "https://portal.staging.safepassageapp.com",
1197
- "https://api.staging.safepassageapp.com"
1198
- ]
984
+ trustedOrigins: ["https://verify.staging.safepassageapp.com"]
1199
985
  }
1200
986
  };
1201
987
  var BRAND_CONSTANTS = {
1202
988
  name: "SafePassage",
1203
- hmacSecretProd: "safepassage-prod-hmac-2025",
1204
- hmacSecretStaging: "safepassage-stage-hmac-2025",
1205
989
  messageType: "safepassage:verification:complete",
1206
990
  legacyMessageType: "safepassage-verification",
1207
991
  popupName: "safepassage-verify",
@@ -1214,7 +998,7 @@ var SafePassage = class extends VerificationSDK {
1214
998
  super(config, BRAND_URLS, BRAND_CONSTANTS);
1215
999
  }
1216
1000
  };
1217
- var VERSION = "3.5.5";
1001
+ var VERSION = "3.5.6";
1218
1002
  SafePassage.VERSION = VERSION;
1219
1003
  if (typeof window !== "undefined") {
1220
1004
  setupPolyfills();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@safepassage/sdk",
3
- "version": "3.5.5",
3
+ "version": "3.5.6",
4
4
  "description": "SafePassage SDK - Lightweight redirect-based age verification",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",