@privateav/sdk 3.5.3 → 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/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # PrivateAV SDK v3.5.3
1
+ # PrivateAV SDK v3.5.6
2
2
 
3
3
  A lightweight SDK for integrating PrivateAV age verification into your website or application.
4
4
 
@@ -9,17 +9,27 @@ A lightweight SDK for integrating PrivateAV age verification into your website o
9
9
  - **Two modes**: Same-tab redirect or new-tab popup
10
10
  - **TypeScript support**: Full type definitions included
11
11
  - **Auto-environment detection**: Works seamlessly across environments
12
- - **Secure**: HMAC-signed state parameters, automatic session management
12
+ - **Server-issued sessions**: launches from the authenticated URL returned by the API
13
13
  - **Compliant**: Enforces minimum age of 25
14
14
 
15
15
  ## Changelog
16
16
 
17
+ ### 3.5.6
18
+ - Removed browser HMAC state. Browser callbacks remain convenience notifications, not proof of verification.
19
+ - Launches now require the server-issued `verifyUrl`; API and Verify UI origins are restricted to the selected brand/environment (localhost overrides remain available for development).
20
+ - Browser SDK verification-policy overrides are retired. Configure policy in the dashboard or create sessions server-side with a private key.
21
+
22
+ ### 3.5.5
23
+ - Routes explicit staging sessions through the canonical VerityGuard-owned staging hosts for the selected SDK brand.
24
+ - Keeps production routing unchanged.
25
+
26
+
17
27
  ### 3.5.3
18
28
  - Fixed environment routing so customer-owned hostnames always default to production infrastructure, even when the hostname contains `staging`/`stage`/`qa`/etc. Internal staging is selected only via an explicit `environment: 'staging'` or a first-party Verity staging host.
19
29
  - No public SDK API changes.
20
30
 
21
31
  ### 3.5.2
22
- - PrivateAV version alignment release to match PrivateAV at `3.5.2`
32
+ - Version alignment release across both brand packages at `3.5.2`
23
33
  - No SDK API changes from `3.5.0`
24
34
 
25
35
  ### 3.5.0
@@ -35,13 +45,13 @@ A lightweight SDK for integrating PrivateAV age verification into your website o
35
45
  ## Installation
36
46
 
37
47
  ```bash
38
- npm install @privateav/sdk
48
+ npm install @privateav/sdk@3.5.6
39
49
  ```
40
50
 
41
51
  Or load directly from jsDelivr CDN (no bundler required):
42
52
 
43
53
  ```html
44
- <script src="https://cdn.jsdelivr.net/npm/@privateav/sdk@latest/privateav.min.js"></script>
54
+ <script src="https://cdn.jsdelivr.net/npm/@privateav/sdk@3.5.6/sdk.min.js"></script>
45
55
  ```
46
56
 
47
57
  ## Quick Start
@@ -63,7 +73,7 @@ await sp.verify();
63
73
  ### With CDN (no bundler)
64
74
 
65
75
  ```html
66
- <script src="https://cdn.jsdelivr.net/npm/@privateav/sdk@latest/privateav.min.js"></script>
76
+ <script src="https://cdn.jsdelivr.net/npm/@privateav/sdk@3.5.6/sdk.min.js"></script>
67
77
  <script>
68
78
  const sp = new PrivateAV({
69
79
  apiKey: 'pk_...',
@@ -85,8 +95,6 @@ That's it! The SDK handles session creation automatically.
85
95
  | cancelUrl | string | No | URL to redirect to if user closes the verification window (new-tab mode) |
86
96
  | environment | string | No | `'production'` or `'staging'` (auto-detected) |
87
97
  | mode | string | No | `'redirect'` (default) or `'new-tab'` |
88
- | defaultChallengeAge | number | No | Default minimum age (25 or higher) |
89
- | defaultVerificationMode | string | No | `'L1'` or `'L2'` |
90
98
  | onComplete | function | No | Callback for new-tab mode |
91
99
  | onCancel | function | No | Called when user closes popup (new-tab mode). Return `false` to suppress automatic `cancelUrl` redirect. |
92
100
  | language | string | No | Default UI language (`'en'`, `'de'`, `'es'`, `'fr'`, `'pt'`, `'it'`). Can be overridden per verification. |
@@ -98,9 +106,6 @@ Override settings per-verification:
98
106
 
99
107
  ```javascript
100
108
  await sp.verify({
101
- challengeAge: 30, // Override minimum age for this session
102
- verificationMode: 'L2', // Force ID verification for this session
103
- faceMatchEnabled: false, // Disable selfie-vs-ID matching where required
104
109
  externalUserId: 'user-123', // Your user ID (returned in webhooks)
105
110
  skipIntro: true, // Skip intro screen
106
111
  autoReturn: true, // Auto-redirect after success
@@ -142,7 +147,7 @@ const sp = new PrivateAV({
142
147
  mode: 'new-tab',
143
148
  onComplete: (result) => {
144
149
  console.log('Verification complete:', result.sessionId, result.status);
145
- // Validate on your server, then update UI
150
+ // Convenience notification only: validate on your server before access
146
151
  },
147
152
  onCancel: () => {
148
153
  console.log('User closed the verification window');
@@ -172,17 +177,20 @@ app.get('/age-verified', async (req, res) => {
172
177
 
173
178
  // Validate with your SECRET key (sk_...)
174
179
  const response = await fetch(
175
- `https://api.privateav.com/api/v1/sessions/${sessionId}`,
180
+ 'https://api.privateav.com/api/v1/sessions/validate',
176
181
  {
182
+ method: 'POST',
177
183
  headers: {
178
- 'Authorization': `Bearer ${process.env.PRIVATEAV_SECRET_KEY}`
179
- }
184
+ 'Content-Type': 'application/json',
185
+ 'X-API-Key': process.env.PRIVATEAV_SECRET_KEY
186
+ },
187
+ body: JSON.stringify({ sessionId })
180
188
  }
181
189
  );
182
190
 
183
191
  const session = await response.json();
184
192
 
185
- if (session.status === 'VERIFIED') {
193
+ if (session.verified && session.accessGranted) {
186
194
  // Grant access
187
195
  req.session.ageVerified = true;
188
196
  res.redirect('/content');
@@ -218,7 +226,7 @@ For reliable verification tracking, configure webhooks in your dashboard:
218
226
  <html>
219
227
  <head>
220
228
  <title>Age Verification</title>
221
- <script src="https://cdn.jsdelivr.net/npm/@privateav/sdk@latest/privateav.min.js"></script>
229
+ <script src="https://cdn.jsdelivr.net/npm/@privateav/sdk@3.5.6/sdk.min.js"></script>
222
230
  </head>
223
231
  <body>
224
232
  <button id="verify-btn">Verify Your Age</button>
@@ -279,6 +287,7 @@ const config: PrivateAVConfig = {
279
287
  returnUrl: '/verified',
280
288
  mode: 'new-tab',
281
289
  onComplete: (result: VerificationResult) => {
290
+ // Convenience notification only; request server-side validation.
282
291
  console.log(`Session ${result.sessionId}: ${result.status}`);
283
292
  }
284
293
  };
@@ -7,6 +7,6 @@ export type PrivateAVConfig = SDKConfig;
7
7
  export declare class PrivateAV extends VerificationSDK {
8
8
  constructor(config: PrivateAVConfig);
9
9
  }
10
- export declare const VERSION = "3.5.3";
10
+ export declare const VERSION = "3.5.6";
11
11
  export type { SDKConfig, VerificationOptions, VerificationResult, SessionValidationResponse, SessionCreationResponse, CreateSessionRequest, };
12
12
  export default PrivateAV;
@@ -19,8 +19,6 @@ export interface BrandUrls {
19
19
  }
20
20
  export interface BrandConstants {
21
21
  name: string;
22
- hmacSecretProd: string;
23
- hmacSecretStaging: string;
24
22
  messageType: string;
25
23
  legacyMessageType?: string;
26
24
  popupName: string;
@@ -45,10 +43,7 @@ export declare class VerificationSDK {
45
43
  private currentSessionId;
46
44
  private hasReceivedResult;
47
45
  private lastVerifyUrl;
48
- private lastSessionToken;
49
46
  private lastExternalUserId;
50
- private lastSandboxMode;
51
- private temporaryHandoffToken;
52
47
  private static readonly LOCAL_HOSTNAMES;
53
48
  /**
54
49
  * Initialize SDK
@@ -62,9 +57,7 @@ export declare class VerificationSDK {
62
57
  * Initiate verification with race condition protection
63
58
  */
64
59
  verify(options?: VerificationOptions): Promise<void>;
65
- /**
66
- * Build verification URL with HMAC-signed state
67
- */
60
+ /** Build a launch URL from the Portal-issued authoritative verify URL. */
68
61
  private buildVerificationUrl;
69
62
  /**
70
63
  * Redirect in same tab
@@ -104,14 +97,7 @@ export declare class VerificationSDK {
104
97
  * Get Portal API URL based on environment and brand
105
98
  */
106
99
  private getPortalApiUrl;
107
- /**
108
- * Get Engine URL based on environment and brand
109
- */
110
- private getEngineUrl;
111
- /**
112
- * Get WebSocket URL based on environment and brand
113
- */
114
- private getWebSocketUrl;
100
+ private validateServiceOverrides;
115
101
  /**
116
102
  * Detect if this is a public key (pk_ prefix) vs private key (sk_ prefix)
117
103
  */
@@ -126,6 +112,6 @@ export declare class VerificationSDK {
126
112
  private getTrustedOrigins;
127
113
  private getAllowedCustomOrigins;
128
114
  private getLocalOrigin;
115
+ private getUrlOrigin;
129
116
  private applyLocalVerifyOverride;
130
- private getHmacSecret;
131
117
  }
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,25 +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) => function __init() {
34
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
35
- };
36
- var __export = (target, all) => {
37
- for (var name in all)
38
- __defProp(target, name, { get: all[name], enumerable: true });
39
- };
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
+ };
38
+ }
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
+ }
40
57
 
41
58
  // src-redirect/utils/security.ts
42
59
  function isOriginTrusted(origin, trustedOrigins) {
@@ -130,6 +147,34 @@ function validateReturnUrl(url, environment, _logLabel = "SDK") {
130
147
  return { isValid: false, error: "Invalid URL format" };
131
148
  }
132
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();
133
178
  function logSecurityEvent(event, metadata, logLabel = "SDK") {
134
179
  const context = {
135
180
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
@@ -138,139 +183,13 @@ function logSecurityEvent(event, metadata, logLabel = "SDK") {
138
183
  };
139
184
  console.warn(`${logLabel} Security Event: ${event}`, __spreadValues(__spreadValues({}, context), metadata));
140
185
  }
141
- var VerificationRateLimit, verificationRateLimit;
142
- var init_security = __esm({
143
- "src-redirect/utils/security.ts"() {
144
- "use strict";
145
- VerificationRateLimit = class {
146
- constructor() {
147
- this.attempts = /* @__PURE__ */ new Map();
148
- this.maxAttempts = 5;
149
- this.timeWindow = 6e4;
150
- }
151
- // 1 minute
152
- isAllowed(identifier, logLabel = "SDK") {
153
- const now = Date.now();
154
- const attempts = this.attempts.get(identifier) || [];
155
- const recentAttempts = attempts.filter(
156
- (time) => now - time < this.timeWindow
157
- );
158
- if (recentAttempts.length >= this.maxAttempts) {
159
- console.warn(
160
- `${logLabel} Security: Rate limit exceeded for ${identifier}`
161
- );
162
- return false;
163
- }
164
- recentAttempts.push(now);
165
- this.attempts.set(identifier, recentAttempts);
166
- return true;
167
- }
168
- reset(identifier) {
169
- this.attempts.delete(identifier);
170
- }
171
- };
172
- verificationRateLimit = new VerificationRateLimit();
173
- }
174
- });
175
-
176
- // src-redirect/utils/crypto.ts
177
- var crypto_exports = {};
178
- __export(crypto_exports, {
179
- createSignedState: () => createSignedState,
180
- generateHMAC: () => generateHMAC,
181
- generateSecureToken: () => generateSecureToken,
182
- parseSignedState: () => parseSignedState,
183
- verifyHMAC: () => verifyHMAC
184
- });
185
- async function generateHMAC(data, secret) {
186
- const encoder = new TextEncoder();
187
- const keyData = encoder.encode(secret);
188
- const dataBuffer = encoder.encode(data);
189
- const key = await crypto.subtle.importKey(
190
- "raw",
191
- keyData,
192
- { name: "HMAC", hash: "SHA-256" },
193
- false,
194
- ["sign"]
195
- );
196
- const signature = await crypto.subtle.sign("HMAC", key, dataBuffer);
197
- return Array.from(new Uint8Array(signature)).map((b) => b.toString(16).padStart(2, "0")).join("");
198
- }
199
- async function verifyHMAC(data, signature, secret) {
200
- try {
201
- const expectedSignature = await generateHMAC(data, secret);
202
- return constantTimeCompare(signature, expectedSignature);
203
- } catch (e) {
204
- return false;
205
- }
206
- }
207
- function constantTimeCompare(a, b) {
208
- if (a.length !== b.length) {
209
- return false;
210
- }
211
- let result = 0;
212
- for (let i = 0; i < a.length; i++) {
213
- result |= a.charCodeAt(i) ^ b.charCodeAt(i);
214
- }
215
- return result === 0;
216
- }
217
- function generateSecureToken(length = 32) {
218
- const array = new Uint8Array(length);
219
- crypto.getRandomValues(array);
220
- return Array.from(array, (b) => b.toString(16).padStart(2, "0")).join("");
221
- }
222
- async function createSignedState(payload, hmacSecret) {
223
- const timestampedPayload = __spreadProps(__spreadValues({}, payload), {
224
- timestamp: Date.now(),
225
- nonce: generateSecureToken(16)
226
- });
227
- const dataString = JSON.stringify(timestampedPayload);
228
- const signature = await generateHMAC(dataString, hmacSecret);
229
- const signedPayload = {
230
- data: timestampedPayload,
231
- signature
232
- };
233
- return btoa(JSON.stringify(signedPayload));
234
- }
235
- async function parseSignedState(signedState, hmacSecret, maxAge = STATE_EXPIRY_MS, logLabel = "SDK") {
236
- try {
237
- const json = atob(signedState);
238
- const signedPayload = JSON.parse(json);
239
- if (!signedPayload.data || !signedPayload.signature) {
240
- console.warn(`${logLabel}: Invalid signed state format`);
241
- return null;
242
- }
243
- const { data, signature } = signedPayload;
244
- const dataString = JSON.stringify(data);
245
- const isValid = await verifyHMAC(dataString, signature, hmacSecret);
246
- if (!isValid) {
247
- console.warn(`${logLabel}: State signature verification failed`);
248
- return null;
249
- }
250
- if (data.timestamp) {
251
- const age = Date.now() - data.timestamp;
252
- if (age > maxAge) {
253
- console.warn(`${logLabel}: State parameter expired`, { age, maxAge });
254
- return null;
255
- }
256
- }
257
- const _a = data, { timestamp, nonce } = _a, payload = __objRest(_a, ["timestamp", "nonce"]);
258
- void timestamp;
259
- void nonce;
260
- return payload;
261
- } catch (error) {
262
- console.warn(`${logLabel}: Failed to parse signed state`, error);
263
- return null;
264
- }
265
- }
266
- var init_crypto = __esm({
267
- "src-redirect/utils/crypto.ts"() {
268
- "use strict";
269
- init_validation();
270
- }
271
- });
272
186
 
273
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_]+$/;
274
193
  function validateConfig(config, context) {
275
194
  var _a;
276
195
  if (!config.apiKey) {
@@ -340,68 +259,6 @@ function validateConfig(config, context) {
340
259
  throw new Error("newTabTarget must be popup or tab");
341
260
  }
342
261
  }
343
- async function generateState(payload, environment, hmacSecret, _logLabel = "SDK") {
344
- const { createSignedState: createSignedState2 } = await Promise.resolve().then(() => (init_crypto(), crypto_exports));
345
- return createSignedState2(payload, hmacSecret);
346
- }
347
- var MINIMUM_AGE, MAXIMUM_AGE, MAX_URL_LENGTH, MAX_API_KEY_LENGTH, STATE_EXPIRY_MS, API_KEY_PATTERN;
348
- var init_validation = __esm({
349
- "src-redirect/utils/validation.ts"() {
350
- "use strict";
351
- init_security();
352
- MINIMUM_AGE = 25;
353
- MAXIMUM_AGE = 150;
354
- MAX_URL_LENGTH = 2048;
355
- MAX_API_KEY_LENGTH = 128;
356
- STATE_EXPIRY_MS = 6e5;
357
- API_KEY_PATTERN = /^(pk_|sk_)[a-zA-Z0-9_]+$/;
358
- }
359
- });
360
-
361
- // src-redirect/utils/polyfills.ts
362
- function setupPolyfills() {
363
- if (!crypto.randomUUID) {
364
- crypto.randomUUID = function() {
365
- const array = new Uint8Array(16);
366
- crypto.getRandomValues(array);
367
- array[6] = array[6] & 15 | 64;
368
- array[8] = array[8] & 63 | 128;
369
- const hex = Array.from(array).map((b) => b.toString(16).padStart(2, "0")).join("");
370
- return [
371
- hex.slice(0, 8),
372
- hex.slice(8, 12),
373
- hex.slice(12, 16),
374
- hex.slice(16, 20),
375
- hex.slice(20, 32)
376
- ].join("-");
377
- };
378
- }
379
- }
380
- function checkBrowserCompatibility(logLabel = "SDK") {
381
- const warnings = [];
382
- if (!window.crypto || !window.crypto.getRandomValues) {
383
- throw new Error(`${logLabel} requires Web Crypto API support`);
384
- }
385
- if (!window.crypto.subtle) {
386
- throw new Error(
387
- `${logLabel} requires Web Crypto subtle API for HMAC operations`
388
- );
389
- }
390
- if (!crypto.randomUUID) {
391
- warnings.push("crypto.randomUUID not supported, using polyfill");
392
- }
393
- if (!window.URLSearchParams) {
394
- warnings.push(
395
- "URLSearchParams not supported, consider adding a polyfill for IE 11 support"
396
- );
397
- }
398
- if (warnings.length > 0) {
399
- console.warn(`${logLabel} Browser Compatibility:`, warnings.join("; "));
400
- }
401
- }
402
-
403
- // src-redirect/core/VerificationSDK.ts
404
- init_validation();
405
262
 
406
263
  // src-redirect/utils/environment.ts
407
264
  function getEnvironmentUrl(environment, urls) {
@@ -520,7 +377,6 @@ function validateEnvironmentSecurity(environment, urls, logLabel = "SDK") {
520
377
  }
521
378
 
522
379
  // src-redirect/core/VerificationSDK.ts
523
- init_security();
524
380
  var _VerificationSDK = class _VerificationSDK {
525
381
  /**
526
382
  * Initialize SDK
@@ -537,16 +393,10 @@ var _VerificationSDK = class _VerificationSDK {
537
393
  this.isVerificationInProgress = false;
538
394
  this.currentSessionId = null;
539
395
  this.hasReceivedResult = false;
540
- // Server-provided verify URL (includes sessionToken)
396
+ // Server-provided verify URL (includes authoritative session context)
541
397
  this.lastVerifyUrl = null;
542
- // Server-provided session token (WS auth)
543
- this.lastSessionToken = null;
544
398
  // External user ID provided during verify() for cancellation redirects
545
399
  this.lastExternalUserId = null;
546
- // Sandbox mode flag from session creation (for UI labeling)
547
- this.lastSandboxMode = null;
548
- // Temporary storage for QR handoff token to include in state
549
- this.temporaryHandoffToken = null;
550
400
  this.brandUrls = brandUrls;
551
401
  this.brandConstants = brandConstants;
552
402
  const environment = resolveEnvironment(config.environment, this.brandUrls);
@@ -565,6 +415,7 @@ var _VerificationSDK = class _VerificationSDK {
565
415
  mode: config.mode || "redirect",
566
416
  newTabTarget: config.newTabTarget || "popup"
567
417
  });
418
+ this.validateServiceOverrides();
568
419
  validateEnvironmentSecurity(this.config.environment, this.getUrlConfig(), this.brandConstants.name);
569
420
  enforceHTTPS(this.config.environment, this.brandConstants.name);
570
421
  logSecurityEvent("SDK_INITIALIZED", {
@@ -643,82 +494,32 @@ var _VerificationSDK = class _VerificationSDK {
643
494
  throw error;
644
495
  }
645
496
  }
646
- /**
647
- * Build verification URL with HMAC-signed state
648
- */
497
+ /** Build a launch URL from the Portal-issued authoritative verify URL. */
649
498
  async buildVerificationUrl(options, sessionId) {
650
- var _a, _b;
651
- const baseUrl = this.config.verifyUrl || getEnvironmentUrl(this.config.environment, this.getUrlConfig());
652
- const hasExplicitChallengeAge = options.challengeAge !== void 0;
653
- const hasExplicitVerificationMode = options.verificationMode !== void 0;
654
- const hasExplicitFaceMatchEnabled = options.faceMatchEnabled !== void 0 || this.config.faceMatchEnabled !== void 0;
655
- const hasOverrides = hasExplicitChallengeAge || hasExplicitVerificationMode || hasExplicitFaceMatchEnabled;
656
- const faceMatchEnabled = (_a = options.faceMatchEnabled) != null ? _a : this.config.faceMatchEnabled;
657
- const state = await generateState(
658
- {
659
- merchantId: this.config.apiKey,
660
- sessionId,
661
- returnUrl: this.config.returnUrl,
662
- cancelUrl: this.config.cancelUrl,
663
- challengeAge: options.challengeAge || this.config.defaultChallengeAge,
664
- verificationMode: options.verificationMode || this.config.defaultVerificationMode,
665
- faceMatchEnabled,
666
- hasOverrides,
667
- // Flag to indicate explicit overrides
668
- externalUserId: options.externalUserId,
669
- timestamp: Date.now(),
670
- // Additional config for self-contained verify-ui
671
- apiUrl: this.getPortalApiUrl(),
672
- engineUrl: this.getEngineUrl(),
673
- wsUrl: this.getWebSocketUrl(),
674
- environment: this.config.environment,
675
- features: {
676
- testMode: false,
677
- warmupPeriodMs: 500,
678
- qualityThreshold: 0.6,
679
- sandboxMode: (_b = this.lastSandboxMode) != null ? _b : false
680
- },
681
- // Include handoffToken if available (for QR code desktop flow)
682
- handoffToken: this.temporaryHandoffToken || void 0,
683
- // Include sessionToken and verifyUrl to make UI auth deterministic
684
- sessionToken: this.lastSessionToken || void 0,
685
- verifyUrl: this.lastVerifyUrl || void 0
686
- },
687
- this.config.environment,
688
- this.getHmacSecret(),
689
- this.brandConstants.name
690
- );
499
+ if (!this.lastVerifyUrl) {
500
+ throw new Error("Server did not return an authoritative verification URL");
501
+ }
691
502
  const language = options.language || this.config.language;
692
- if (this.lastVerifyUrl) {
693
- try {
694
- const resolvedVerifyUrl = this.applyLocalVerifyOverride(this.lastVerifyUrl);
695
- const url = new URL(resolvedVerifyUrl);
696
- url.searchParams.set("state", state);
697
- url.searchParams.set("mode", this.config.mode);
698
- if (options.skipIntro) {
699
- url.searchParams.set("skip_intro", "true");
700
- }
701
- if (options.autoReturn) {
702
- url.searchParams.set("auto_return", "true");
703
- }
704
- if (language) {
705
- url.searchParams.set("lang", language);
706
- }
707
- return url.toString();
708
- } catch (e) {
709
- }
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");
710
507
  }
711
- 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);
712
513
  if (options.skipIntro) {
713
- params.set("skip_intro", "true");
514
+ url.searchParams.set("skip_intro", "true");
714
515
  }
715
516
  if (options.autoReturn) {
716
- params.set("auto_return", "true");
517
+ url.searchParams.set("auto_return", "true");
717
518
  }
718
519
  if (language) {
719
- params.set("lang", language);
520
+ url.searchParams.set("lang", language);
720
521
  }
721
- return `${baseUrl}/?${params.toString()}`;
522
+ return url.toString();
722
523
  }
723
524
  /**
724
525
  * Redirect in same tab
@@ -976,22 +777,33 @@ var _VerificationSDK = class _VerificationSDK {
976
777
  * Get Portal API URL based on environment and brand
977
778
  */
978
779
  getPortalApiUrl() {
979
- 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)) {
980
787
  return this.config.apiUrl;
981
788
  }
982
- return this.getUrlConfig().apiUrl;
789
+ throw new Error("apiUrl must use the selected brand/environment service");
983
790
  }
984
- /**
985
- * Get Engine URL based on environment and brand
986
- */
987
- getEngineUrl() {
988
- return this.getUrlConfig().engineUrl;
989
- }
990
- /**
991
- * Get WebSocket URL based on environment and brand
992
- */
993
- getWebSocketUrl() {
994
- 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
+ }
995
807
  }
996
808
  /**
997
809
  * Detect if this is a public key (pk_ prefix) vs private key (sk_ prefix)
@@ -1003,7 +815,7 @@ var _VerificationSDK = class _VerificationSDK {
1003
815
  * Create session internally for public keys
1004
816
  */
1005
817
  async createInternalSession(options) {
1006
- var _a, _b, _c;
818
+ var _a, _b;
1007
819
  try {
1008
820
  const portalApiUrl = this.getPortalApiUrl();
1009
821
  const response = await fetch(`${portalApiUrl}/api/v1/sessions/create`, {
@@ -1016,9 +828,6 @@ var _VerificationSDK = class _VerificationSDK {
1016
828
  merchantId: this.config.apiKey,
1017
829
  returnUrl: this.config.returnUrl,
1018
830
  cancelUrl: this.config.cancelUrl,
1019
- challengeAge: options.challengeAge,
1020
- verificationMode: options.verificationMode,
1021
- faceMatchEnabled: (_a = options.faceMatchEnabled) != null ? _a : this.config.faceMatchEnabled,
1022
831
  merchantName: document.title || window.location.hostname,
1023
832
  externalUserId: options.externalUserId
1024
833
  })
@@ -1028,7 +837,7 @@ var _VerificationSDK = class _VerificationSDK {
1028
837
  const errorCode = errorData == null ? void 0 : errorData.code;
1029
838
  if (this.isBillingBlockError(errorCode)) {
1030
839
  const language = options.language || this.config.language;
1031
- this.openBillingBlockPage(errorCode, errorData == null ? void 0 : errorData.portalUrl, language);
840
+ this.openBillingBlockPage(errorCode, language);
1032
841
  }
1033
842
  throw new Error(
1034
843
  `Failed to create session: ${response.status} ${response.statusText}. ${errorData.message || ""}`
@@ -1042,17 +851,6 @@ var _VerificationSDK = class _VerificationSDK {
1042
851
  if (sessionData.verifyUrl) {
1043
852
  this.lastVerifyUrl = sessionData.verifyUrl;
1044
853
  }
1045
- if (sessionData.sessionToken) {
1046
- this.lastSessionToken = sessionData.sessionToken;
1047
- }
1048
- if (sessionData.handoffToken) {
1049
- this.temporaryHandoffToken = sessionData.handoffToken;
1050
- }
1051
- if (typeof sessionData.sandboxMode === "boolean") {
1052
- this.lastSandboxMode = sessionData.sandboxMode;
1053
- } else {
1054
- this.lastSandboxMode = null;
1055
- }
1056
854
  logSecurityEvent("INTERNAL_SESSION_CREATED", {
1057
855
  sessionId: sessionId.substring(0, 8) + "...",
1058
856
  environment: this.config.environment,
@@ -1066,23 +864,20 @@ var _VerificationSDK = class _VerificationSDK {
1066
864
  environment: this.config.environment,
1067
865
  apiKeyType: "public"
1068
866
  }, this.brandConstants.name);
1069
- (_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);
1070
868
  throw new Error(`Failed to create verification session: ${errorMessage}`);
1071
869
  }
1072
870
  }
1073
871
  isBillingBlockError(code) {
1074
872
  return code === "SUBSCRIPTION_REQUIRED" || code === "PLAN_LIMIT_REACHED" || code === "SANDBOX_LIMIT_REACHED";
1075
873
  }
1076
- openBillingBlockPage(code, portalUrl, language) {
874
+ openBillingBlockPage(code, language) {
1077
875
  var _a, _b, _c, _d;
1078
876
  try {
1079
- const baseUrl = this.config.verifyUrl || getEnvironmentUrl(this.config.environment, this.getUrlConfig());
877
+ const baseUrl = getEnvironmentUrl(this.config.environment, this.getUrlConfig());
1080
878
  const resolvedUrl = this.applyLocalVerifyOverride(baseUrl);
1081
879
  const url = new URL(resolvedUrl);
1082
880
  url.searchParams.set("blocked", code);
1083
- if (portalUrl) {
1084
- url.searchParams.set("portalUrl", portalUrl);
1085
- }
1086
881
  const effectiveLanguage = language || this.config.language;
1087
882
  if (effectiveLanguage) {
1088
883
  url.searchParams.set("lang", effectiveLanguage);
@@ -1145,6 +940,13 @@ var _VerificationSDK = class _VerificationSDK {
1145
940
  }
1146
941
  return null;
1147
942
  }
943
+ getUrlOrigin(urlValue) {
944
+ try {
945
+ return new URL(urlValue).origin;
946
+ } catch (e) {
947
+ return null;
948
+ }
949
+ }
1148
950
  applyLocalVerifyOverride(rawUrl) {
1149
951
  const overrideOrigin = this.getLocalOrigin(this.config.verifyUrl || null);
1150
952
  if (!overrideOrigin) {
@@ -1160,9 +962,6 @@ var _VerificationSDK = class _VerificationSDK {
1160
962
  return rawUrl;
1161
963
  }
1162
964
  }
1163
- getHmacSecret() {
1164
- return this.config.environment === "staging" ? this.brandConstants.hmacSecretStaging : this.brandConstants.hmacSecretProd;
1165
- }
1166
965
  };
1167
966
  // Local override hostnames allowed for internal testing
1168
967
  _VerificationSDK.LOCAL_HOSTNAMES = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "::1"]);
@@ -1175,28 +974,18 @@ var BRAND_URLS = {
1175
974
  verifyUiUrl: "https://verify.privateav.com",
1176
975
  engineUrl: "https://engine.privateav.com",
1177
976
  wsUrl: "wss://engine.privateav.com/api/websocket/stream",
1178
- trustedOrigins: [
1179
- "https://verify.privateav.com",
1180
- "https://portal.privateav.com",
1181
- "https://api.privateav.com"
1182
- ]
977
+ trustedOrigins: ["https://verify.privateav.com"]
1183
978
  },
1184
979
  staging: {
1185
- apiUrl: "https://api.verityav-staging-usw1a.privateav.com",
1186
- verifyUiUrl: "https://verify.verityav-staging-usw1a.privateav.com",
1187
- engineUrl: "https://engine.verityav-staging-usw1a.privateav.com",
1188
- wsUrl: "wss://engine.verityav-staging-usw1a.privateav.com/api/websocket/stream",
1189
- trustedOrigins: [
1190
- "https://verify.verityav-staging-usw1a.privateav.com",
1191
- "https://portal.verityav-staging-usw1a.privateav.com",
1192
- "https://api.verityav-staging-usw1a.privateav.com"
1193
- ]
980
+ apiUrl: "https://api.staging.privateav.com",
981
+ verifyUiUrl: "https://verify.staging.privateav.com",
982
+ engineUrl: "https://engine.staging.privateav.com",
983
+ wsUrl: "wss://engine.staging.privateav.com/api/websocket/stream",
984
+ trustedOrigins: ["https://verify.staging.privateav.com"]
1194
985
  }
1195
986
  };
1196
987
  var BRAND_CONSTANTS = {
1197
988
  name: "PrivateAV",
1198
- hmacSecretProd: "privateav-prod-hmac-2025",
1199
- hmacSecretStaging: "privateav-stage-hmac-2025",
1200
989
  messageType: "privateav:verification:complete",
1201
990
  legacyMessageType: "privateav-verification",
1202
991
  popupName: "privateav-verify",
@@ -1209,7 +998,7 @@ var PrivateAV = class extends VerificationSDK {
1209
998
  super(config, BRAND_URLS, BRAND_CONSTANTS);
1210
999
  }
1211
1000
  };
1212
- var VERSION = "3.5.3";
1001
+ var VERSION = "3.5.6";
1213
1002
  PrivateAV.VERSION = VERSION;
1214
1003
  if (typeof window !== "undefined") {
1215
1004
  setupPolyfills();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@privateav/sdk",
3
- "version": "3.5.3",
3
+ "version": "3.5.6",
4
4
  "description": "PrivateAV SDK - Lightweight redirect-based age verification",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
package/privateav.min.js CHANGED
@@ -1,3 +1,3 @@
1
- /* PrivateAV SDK v3.5.3 */
2
- "use strict";var PrivateAVSDK=(()=>{var S=Object.defineProperty,pe=Object.defineProperties,ue=Object.getOwnPropertyDescriptor,fe=Object.getOwnPropertyDescriptors,he=Object.getOwnPropertyNames,y=Object.getOwnPropertySymbols;var T=Object.prototype.hasOwnProperty,W=Object.prototype.propertyIsEnumerable;var B=(t,e,n)=>e in t?S(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n,v=(t,e)=>{for(var n in e||(e={}))T.call(e,n)&&B(t,n,e[n]);if(y)for(var n of y(e))W.call(e,n)&&B(t,n,e[n]);return t},U=(t,e)=>pe(t,fe(e));var H=(t,e)=>{var n={};for(var r in t)T.call(t,r)&&e.indexOf(r)<0&&(n[r]=t[r]);if(t!=null&&y)for(var r of y(t))e.indexOf(r)<0&&W.call(t,r)&&(n[r]=t[r]);return n};var A=(t,e)=>()=>(t&&(e=t(t=0)),e);var F=(t,e)=>{for(var n in e)S(t,n,{get:e[n],enumerable:!0})},me=(t,e,n,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of he(e))!T.call(t,i)&&i!==n&&S(t,i,{get:()=>e[i],enumerable:!(r=ue(e,i))||r.enumerable});return t};var ve=t=>me(S({},"__esModule",{value:!0}),t);function we(t,e){return e.includes(t)}function G(t,e,n=[],r="SDK"){var s;let{origin:i}=t;return we(i,e)||n.length>0&&n.some(a=>{if(a.startsWith("*.")){let c=a.slice(2);return i.endsWith(`.${c}`)||i===`https://${c}`||i===`http://${c}`}return i===a})?!0:(console.warn(`${r} Security: Blocked PostMessage from untrusted origin: ${i}`,{trustedOrigins:e,allowedCustomOrigins:n,eventType:(s=t.data)==null?void 0:s.type}),!1)}function J(t,e,n,r){let{data:i}=t;return!i||typeof i!="object"?{isValid:!1,error:"Invalid message format"}:(r?[n,r]:[n]).includes(i.type)?!i.sessionId||i.sessionId!==e?{isValid:!1,error:"Session ID mismatch"}:!i.status||!["verified","failed","cancelled"].includes(i.status)?{isValid:!1,error:"Invalid status value"}:{isValid:!0}:{isValid:!1,error:"Invalid message type"}}function X(t,e="SDK"){t==="production"&&window.location.protocol!=="https:"&&console.warn(`${e} Warning: HTTPS recommended for production environment`,{current:window.location.href})}function x(t,e,n="SDK"){try{let r=new URL(t);if(r.protocol==="file:")return{isValid:!1,error:"file:// URLs are not supported. The verification redirect cannot return to local files. Please use a local web server (e.g., npx serve . or python3 -m http.server) instead of opening the HTML file directly."};if(r.protocol!=="https:"&&!(r.hostname==="localhost"||r.hostname==="127.0.0.1"))return{isValid:!1,error:`HTTPS required for return URLs in ${e}`};let i=[/data:/i,/javascript:/i,/vbscript:/i,/file:/i,/ftp:/i];for(let s of i)if(s.test(t))return{isValid:!1,error:"Blocked suspicious URL scheme"};return{isValid:!0}}catch(r){return{isValid:!1,error:"Invalid URL format"}}}function p(t,e,n="SDK"){let r={timestamp:new Date().toISOString(),userAgent:typeof navigator!="undefined"?navigator.userAgent:"unknown",url:typeof window!="undefined"?window.location.href:"unknown"};console.warn(`${n} Security Event: ${t}`,v(v({},r),e))}var P,Y,R=A(()=>{"use strict";P=class{constructor(){this.attempts=new Map;this.maxAttempts=5;this.timeWindow=6e4}isAllowed(e,n="SDK"){let r=Date.now(),s=(this.attempts.get(e)||[]).filter(l=>r-l<this.timeWindow);return s.length>=this.maxAttempts?(console.warn(`${n} Security: Rate limit exceeded for ${e}`),!1):(s.push(r),this.attempts.set(e,s),!0)}reset(e){this.attempts.delete(e)}},Y=new P});var Q={};F(Q,{createSignedState:()=>Se,generateHMAC:()=>M,generateSecureToken:()=>z,parseSignedState:()=>Ue,verifyHMAC:()=>Z});async function M(t,e){let n=new TextEncoder,r=n.encode(e),i=n.encode(t),s=await crypto.subtle.importKey("raw",r,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),l=await crypto.subtle.sign("HMAC",s,i);return Array.from(new Uint8Array(l)).map(a=>a.toString(16).padStart(2,"0")).join("")}async function Z(t,e,n){try{let r=await M(t,n);return ye(e,r)}catch(r){return!1}}function ye(t,e){if(t.length!==e.length)return!1;let n=0;for(let r=0;r<t.length;r++)n|=t.charCodeAt(r)^e.charCodeAt(r);return n===0}function z(t=32){let e=new Uint8Array(t);return crypto.getRandomValues(e),Array.from(e,n=>n.toString(16).padStart(2,"0")).join("")}async function Se(t,e){let n=U(v({},t),{timestamp:Date.now(),nonce:z(16)}),r=JSON.stringify(n),i=await M(r,e);return btoa(JSON.stringify({data:n,signature:i}))}async function Ue(t,e,n=te,r="SDK"){try{let s=atob(t),l=JSON.parse(s);if(!l.data||!l.signature)return console.warn(`${r}: Invalid signed state format`),null;let{data:a,signature:c}=l,d=JSON.stringify(a);if(!await Z(d,c,e))return console.warn(`${r}: State signature verification failed`),null;if(a.timestamp){let h=Date.now()-a.timestamp;if(h>n)return console.warn(`${r}: State parameter expired`,{age:h,maxAge:n}),null}let i=a,{timestamp:g,nonce:f}=i;return H(i,["timestamp","nonce"])}catch(s){return console.warn(`${r}: Failed to parse signed state`,s),null}}var ee=A(()=>{"use strict";V()});function se(t,e){var i;if(!t.apiKey)throw new Error("apiKey is required");if(t.apiKey.length>ie)throw new Error(`apiKey exceeds maximum length of ${ie} characters`);if(!be.test(t.apiKey))throw new Error("Invalid apiKey format. Expected pk_xxx (public) or sk_xxx (private)");if(typeof window!="undefined"&&t.apiKey.startsWith("sk_"))throw new Error(`Secret keys (sk_) should never be used in browser code for security reasons. Secret keys expose your account to unauthorized access if used client-side. Please use your public key (pk_) instead. If you need to use features that require a secret key (like custom challenge age), create the session server-side and pass the sessionId to startVerificationWithSession(). See: ${e.docsUrl}/server-side-sessions`);if(!t.returnUrl)throw new Error("returnUrl is required");if(t.returnUrl.length>b)throw new Error(`returnUrl exceeds maximum length of ${b} characters`);let n=(i=e.environment)!=null?i:"production",r=x(t.returnUrl,n,e.brandName);if(!r.isValid)throw new Error(`returnUrl validation failed: ${r.error}`);if(t.cancelUrl){if(t.cancelUrl.length>b)throw new Error(`cancelUrl exceeds maximum length of ${b} characters`);let s=x(t.cancelUrl,n,e.brandName);if(!s.isValid)throw new Error(`cancelUrl validation failed: ${s.error}`)}if(t.defaultChallengeAge!==void 0){if(t.defaultChallengeAge<ne)throw new Error(`defaultChallengeAge must be at least ${ne}`);if(t.defaultChallengeAge>re)throw new Error(`defaultChallengeAge cannot exceed ${re}`)}if(t.defaultVerificationMode&&!["L1","L2"].includes(t.defaultVerificationMode))throw new Error("defaultVerificationMode must be L1 or L2");if(t.faceMatchEnabled!==void 0&&typeof t.faceMatchEnabled!="boolean")throw new Error("faceMatchEnabled must be a boolean");if(t.mode&&!["redirect","new-tab"].includes(t.mode))throw new Error("mode must be redirect or new-tab");if(t.newTabTarget&&!["popup","tab"].includes(t.newTabTarget))throw new Error("newTabTarget must be popup or tab")}async function oe(t,e,n,r="SDK"){let{createSignedState:i}=await Promise.resolve().then(()=>(ee(),Q));return i(t,n)}var ne,re,b,ie,te,be,V=A(()=>{"use strict";R();ne=25,re=150,b=2048,ie=128,te=6e5,be=/^(pk_|sk_)[a-zA-Z0-9_]+$/});var xe={};F(xe,{PrivateAV:()=>w,VERSION:()=>ge,default:()=>Pe});function j(){crypto.randomUUID||(crypto.randomUUID=function(){let t=new Uint8Array(16);crypto.getRandomValues(t),t[6]=t[6]&15|64,t[8]=t[8]&63|128;let e=Array.from(t).map(n=>n.toString(16).padStart(2,"0")).join("");return[e.slice(0,8),e.slice(8,12),e.slice(12,16),e.slice(16,20),e.slice(20,32)].join("-")})}function q(t="SDK"){let e=[];if(!window.crypto||!window.crypto.getRandomValues)throw new Error(`${t} requires Web Crypto API support`);if(!window.crypto.subtle)throw new Error(`${t} requires Web Crypto subtle API for HMAC operations`);crypto.randomUUID||e.push("crypto.randomUUID not supported, using polyfill"),window.URLSearchParams||e.push("URLSearchParams not supported, consider adding a polyfill for IE 11 support"),e.length>0&&console.warn(`${t} Browser Compatibility:`,e.join("; "))}V();function E(t,e){let n=e.verifyUiUrl;if(!n||!n.startsWith("https://"))throw new Error(`HTTPS required for ${t} environment`);return n}function Ee(t,e){let n=e.apiUrl;if(!n||!n.startsWith("https://"))throw new Error(`HTTPS required for API URLs in ${t} environment`);return n}function Ce(t){if(!t)return null;try{return new URL(t).hostname.toLowerCase()}catch(e){return null}}function Ie(t){let e=t.split(".");return e.length<=2?t:e.slice(1).join(".")}function ae(t){let e=new Set;for(let n of t){let r=Ce(n);r&&e.add(Ie(r))}return e}function Te(t){var s,l;if(!t||!t.staging)return[];let e=t.staging,n=ae([e.apiUrl,e.verifyUiUrl,e.engineUrl,e.wsUrl,...(s=e.trustedOrigins)!=null?s:[]]),r=t.production,i=r?ae([r.apiUrl,r.verifyUiUrl,r.engineUrl,r.wsUrl,...(l=r.trustedOrigins)!=null?l:[]]):new Set;for(let a of i)n.delete(a);return Array.from(n)}function Ae(t,e){let n=(t||"").toLowerCase();return n?Te(e).some(r=>n===r||n.endsWith(`.${r}`)):!1}function le(t,e){return t==="staging"||t==="production"?t:t||typeof window=="undefined"||!window.location?"production":Ae(window.location.hostname,e)?"staging":"production"}function ce(t,e,n="SDK"){let r=window.location.protocol==="https:";switch(t){case"production":r||console.warn(`${n} Warning: HTTPS recommended for production environment`);break;case"staging":r||console.warn(`${n} Warning: HTTPS strongly recommended in staging environment`);break}try{E(t,e),Ee(t,e)}catch(i){let s=i instanceof Error?i.message:String(i);throw new Error(`Environment configuration validation failed: ${s}`)}}R();var I=class I{constructor(e,n,r){this.popupWindow=null;this.messageListener=null;this.popupMonitorInterval=null;this.unloadListener=null;this.isVerificationInProgress=!1;this.currentSessionId=null;this.hasReceivedResult=!1;this.lastVerifyUrl=null;this.lastSessionToken=null;this.lastExternalUserId=null;this.lastSandboxMode=null;this.temporaryHandoffToken=null;this.brandUrls=n,this.brandConstants=r;let i=le(e.environment,this.brandUrls);e.environment&&e.environment!=="staging"&&e.environment!=="production"&&console.warn(`${this.brandConstants.name} SDK: Unknown environment '${e.environment}', defaulting to 'production'`),se(e,{brandName:this.brandConstants.name,docsUrl:this.brandConstants.docsUrl,environment:i}),this.config=U(v({},e),{environment:i,mode:e.mode||"redirect",newTabTarget:e.newTabTarget||"popup"}),ce(this.config.environment,this.getUrlConfig(),this.brandConstants.name),X(this.config.environment,this.brandConstants.name),p("SDK_INITIALIZED",{environment:this.config.environment,mode:this.config.mode,origin:window.location.origin,protocol:window.location.protocol,hostname:window.location.hostname},this.brandConstants.name),this.setupAutoCleanup()}async verify(e={}){var i,s,l,a,c,d;let n=this.isPublicKey(),r;if(n)r=await this.createInternalSession(e);else throw new Error("Private API keys (sk_) should use the direct API, not the SDK. The SDK is designed for browser-based public key usage only.");if(!r)throw new Error("Failed to obtain sessionId from server");if(this.isVerificationInProgress){let o=new Error(`Verification already in progress for session ${(i=this.currentSessionId)==null?void 0:i.substring(0,8)}...`);throw p("RACE_CONDITION_PREVENTED",{currentSession:((s=this.currentSessionId)==null?void 0:s.substring(0,8))+"...",attemptedSession:"new-session-attempt",origin:window.location.origin},this.brandConstants.name),(a=(l=this.config).onError)==null||a.call(l,o),o}this.isVerificationInProgress=!0,this.currentSessionId=r,this.lastExternalUserId=e.externalUserId||null;try{let o=`${this.config.apiKey}:${window.location.origin}`;if(!Y.isAllowed(o,this.brandConstants.name)){let f=new Error("Too many verification attempts. Please wait before trying again.");throw p("RATE_LIMIT_EXCEEDED",{apiKey:this.config.apiKey.substring(0,8)+"...",origin:window.location.origin,sessionId:r?r.substring(0,8)+"...":"undefined"},this.brandConstants.name),(d=(c=this.config).onError)==null||d.call(c,f),f}let g=await this.buildVerificationUrl(e,r);p("VERIFICATION_INITIATED",{environment:this.config.environment,mode:this.config.mode,sessionId:r?r.substring(0,8)+"...":"undefined",origin:window.location.origin},this.brandConstants.name),this.config.mode==="new-tab"?this.openNewTab(g,r):(this.unlockVerification(),this.redirect(g))}catch(o){throw this.unlockVerification(),o}}async buildVerificationUrl(e,n){var f,m;let r=this.config.verifyUrl||E(this.config.environment,this.getUrlConfig()),i=e.challengeAge!==void 0,s=e.verificationMode!==void 0,l=e.faceMatchEnabled!==void 0||this.config.faceMatchEnabled!==void 0,a=i||s||l,c=(f=e.faceMatchEnabled)!=null?f:this.config.faceMatchEnabled,d=await oe({merchantId:this.config.apiKey,sessionId:n,returnUrl:this.config.returnUrl,cancelUrl:this.config.cancelUrl,challengeAge:e.challengeAge||this.config.defaultChallengeAge,verificationMode:e.verificationMode||this.config.defaultVerificationMode,faceMatchEnabled:c,hasOverrides:a,externalUserId:e.externalUserId,timestamp:Date.now(),apiUrl:this.getPortalApiUrl(),engineUrl:this.getEngineUrl(),wsUrl:this.getWebSocketUrl(),environment:this.config.environment,features:{testMode:!1,warmupPeriodMs:500,qualityThreshold:.6,sandboxMode:(m=this.lastSandboxMode)!=null?m:!1},handoffToken:this.temporaryHandoffToken||void 0,sessionToken:this.lastSessionToken||void 0,verifyUrl:this.lastVerifyUrl||void 0},this.config.environment,this.getHmacSecret(),this.brandConstants.name),o=e.language||this.config.language;if(this.lastVerifyUrl)try{let h=this.applyLocalVerifyOverride(this.lastVerifyUrl),u=new URL(h);return u.searchParams.set("state",d),u.searchParams.set("mode",this.config.mode),e.skipIntro&&u.searchParams.set("skip_intro","true"),e.autoReturn&&u.searchParams.set("auto_return","true"),o&&u.searchParams.set("lang",o),u.toString()}catch(h){}let g=new URLSearchParams({state:d,sessionId:n,mode:this.config.mode});return e.skipIntro&&g.set("skip_intro","true"),e.autoReturn&&g.set("auto_return","true"),o&&g.set("lang",o),`${r}/?${g.toString()}`}redirect(e){window.location.href=e}openNewTab(e,n){var c,d;if(this.cleanup(),this.hasReceivedResult=!1,this.popupMonitorInterval&&(clearInterval(this.popupMonitorInterval),this.popupMonitorInterval=null),(this.config.newTabTarget||"popup")==="tab"?this.popupWindow=window.open(e,"_blank"):this.popupWindow=window.open(e,this.brandConstants.popupName,"width=600,height=700"),!this.popupWindow){(d=(c=this.config).onError)==null||d.call(c,new Error("Failed to open verification window. Please check popup blocker settings."));return}let i=this.getTrustedOrigins(),s=this.getAllowedCustomOrigins(e),l=this.brandConstants.messageType,a=this.brandConstants.legacyMessageType;this.messageListener=o=>{var k,D,_,O,$,N,K;let g=(k=o.data)==null?void 0:k.type;if(!g||typeof g!="string"||!(a?[l,a]:[l]).includes(g))return;if(!G(o,i,s,this.brandConstants.name)){p("POSTMESSAGE_ORIGIN_BLOCKED",{origin:o.origin,environment:this.config.environment,expectedOrigins:`${this.brandConstants.name} trusted origins for ${this.config.environment}`,messageType:(D=o.data)==null?void 0:D.type},this.brandConstants.name);return}let m=J(o,n,l,a);if(!m.isValid){p("POSTMESSAGE_VALIDATION_FAILED",{error:m.error,origin:o.origin,sessionId:n.substring(0,8)+"...",messageType:(_=o.data)==null?void 0:_.type},this.brandConstants.name);return}let h=o.data.status;if(h==="cancelled"){this.handleCancellation(n,"postmessage");return}let u={sessionId:o.data.sessionId,status:h,timestamp:o.data.timestamp,externalUserId:o.data.externalUserId};this.hasReceivedResult=!0,p("VERIFICATION_COMPLETED",{status:u.status,sessionId:n.substring(0,8)+"...",origin:o.origin},this.brandConstants.name),this.cleanup({closePopup:!1}),this.unlockVerification(),this.popupMonitorInterval&&(clearInterval(this.popupMonitorInterval),this.popupMonitorInterval=null),u.status==="verified"?($=(O=this.config).onComplete)==null||$.call(O,u):(K=(N=this.config).onError)==null||K.call(N,new Error(`Verification failed: ${u.status}`))},window.addEventListener("message",this.messageListener),this.popupMonitorInterval=setInterval(()=>{this.popupWindow&&this.popupWindow.closed&&(p("POPUP_CLOSED_BY_USER",{sessionId:n.substring(0,8)+"...",environment:this.config.environment},this.brandConstants.name),this.hasReceivedResult||this.handleCancellation(n,"popup-closed"))},500)}setupAutoCleanup(){if(this.unloadListener=()=>{p("SDK_AUTO_CLEANUP",{environment:this.config.environment,trigger:"page_unload"},this.brandConstants.name),this.cleanup(),this.unlockVerification()},window.addEventListener("beforeunload",this.unloadListener),window.addEventListener("pagehide",this.unloadListener),window.history&&window.history.pushState){let e=window.history.pushState;window.history.pushState=(...n)=>(this.cleanup(),this.unlockVerification(),e.apply(window.history,n))}}getEnvironment(){return this.config.environment}unlockVerification(){this.isVerificationInProgress=!1,this.currentSessionId=null,p("VERIFICATION_UNLOCKED",{environment:this.config.environment,origin:window.location.origin},this.brandConstants.name)}cleanup(e={}){let n=e.closePopup!==!1;this.popupWindow&&(n&&!this.popupWindow.closed&&this.popupWindow.close(),(n||this.popupWindow.closed)&&(this.popupWindow=null)),this.messageListener&&(window.removeEventListener("message",this.messageListener),this.messageListener=null),this.popupMonitorInterval&&(clearInterval(this.popupMonitorInterval),this.popupMonitorInterval=null)}handleCancellation(e,n){if(this.hasReceivedResult)return;this.hasReceivedResult=!0,p("VERIFICATION_CANCELLED",{source:n,sessionId:e.substring(0,8)+"...",environment:this.config.environment,origin:window.location.origin},this.brandConstants.name),this.cleanup(),this.unlockVerification();let r=!0;if(this.config.onCancel)try{this.config.onCancel()===!1&&(r=!1)}catch(i){p("CANCEL_CALLBACK_FAILED",{error:i instanceof Error?i.message:String(i),sessionId:e.substring(0,8)+"..."},this.brandConstants.name)}r&&this.redirectToCancelUrl(e)}redirectToCancelUrl(e){if(this.config.cancelUrl)try{let n=decodeURIComponent(this.config.cancelUrl),r=new URL(n);r.searchParams.set("sessionId",e),r.searchParams.set("status","cancelled"),r.searchParams.set("timestamp",Date.now().toString()),this.lastExternalUserId&&r.searchParams.set("externalUserId",this.lastExternalUserId),window.location.href=r.toString()}catch(n){p("CANCEL_REDIRECT_FAILED",{error:n instanceof Error?n.message:String(n),sessionId:e.substring(0,8)+"..."},this.brandConstants.name)}}removeAutoCleanupListeners(){this.unloadListener&&(window.removeEventListener("beforeunload",this.unloadListener),window.removeEventListener("pagehide",this.unloadListener),this.unloadListener=null)}destroy(){p("SDK_DESTROYED",{environment:this.config.environment,origin:window.location.origin},this.brandConstants.name),this.cleanup(),this.unlockVerification(),this.removeAutoCleanupListeners()}getPortalApiUrl(){return this.config.apiUrl?this.config.apiUrl:this.getUrlConfig().apiUrl}getEngineUrl(){return this.getUrlConfig().engineUrl}getWebSocketUrl(){return this.getUrlConfig().wsUrl}isPublicKey(){return this.config.apiKey.startsWith("pk_")}async createInternalSession(e){var n,r,i;try{let s=this.getPortalApiUrl(),l=await fetch(`${s}/api/v1/sessions/create`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.config.apiKey}`},body:JSON.stringify({merchantId:this.config.apiKey,returnUrl:this.config.returnUrl,cancelUrl:this.config.cancelUrl,challengeAge:e.challengeAge,verificationMode:e.verificationMode,faceMatchEnabled:(n=e.faceMatchEnabled)!=null?n:this.config.faceMatchEnabled,merchantName:document.title||window.location.hostname,externalUserId:e.externalUserId})});if(!l.ok){let d=await l.json().catch(()=>({})),o=d==null?void 0:d.code;if(this.isBillingBlockError(o)){let g=e.language||this.config.language;this.openBillingBlockPage(o,d==null?void 0:d.portalUrl,g)}throw new Error(`Failed to create session: ${l.status} ${l.statusText}. ${d.message||""}`)}let a=await l.json(),c=a.sessionId;if(!c)throw new Error("Server did not return a sessionId");return a.verifyUrl&&(this.lastVerifyUrl=a.verifyUrl),a.sessionToken&&(this.lastSessionToken=a.sessionToken),a.handoffToken&&(this.temporaryHandoffToken=a.handoffToken),typeof a.sandboxMode=="boolean"?this.lastSandboxMode=a.sandboxMode:this.lastSandboxMode=null,p("INTERNAL_SESSION_CREATED",{sessionId:c.substring(0,8)+"...",environment:this.config.environment,apiKeyType:"public"},this.brandConstants.name),c}catch(s){let l=s instanceof Error?s.message:String(s);throw p("INTERNAL_SESSION_FAILED",{error:l,environment:this.config.environment,apiKeyType:"public"},this.brandConstants.name),(i=(r=this.config).onError)==null||i.call(r,s),new Error(`Failed to create verification session: ${l}`)}}isBillingBlockError(e){return e==="SUBSCRIPTION_REQUIRED"||e==="PLAN_LIMIT_REACHED"||e==="SANDBOX_LIMIT_REACHED"}openBillingBlockPage(e,n,r){var i,s,l,a;try{let c=this.config.verifyUrl||E(this.config.environment,this.getUrlConfig()),d=this.applyLocalVerifyOverride(c),o=new URL(d);o.searchParams.set("blocked",e),n&&o.searchParams.set("portalUrl",n);let g=r||this.config.language;if(g&&o.searchParams.set("lang",g),this.config.mode==="new-tab"){((this.config.newTabTarget||"popup")==="tab"?window.open(o.toString(),"_blank"):window.open(o.toString(),this.brandConstants.popupName,"width=600,height=700"))||(s=(i=this.config).onError)==null||s.call(i,new Error("Failed to open billing notice window. Please check popup blocker settings."));return}this.redirect(o.toString())}catch(c){let d=c instanceof Error?c.message:String(c);(a=(l=this.config).onError)==null||a.call(l,new Error(`Failed to open billing notice: ${d}`))}}getUrlConfig(){return this.brandUrls[this.config.environment]||this.brandUrls.production}getTrustedOrigins(){return this.getUrlConfig().trustedOrigins}getAllowedCustomOrigins(e){let n=new Set,r=this.getLocalOrigin(this.config.verifyUrl||null),i=this.getLocalOrigin(e||null);return r&&n.add(r),i&&n.add(i),Array.from(n)}getLocalOrigin(e){if(!e)return null;try{let n=new URL(e);if(I.LOCAL_HOSTNAMES.has(n.hostname))return n.origin}catch(n){return null}return null}applyLocalVerifyOverride(e){let n=this.getLocalOrigin(this.config.verifyUrl||null);if(!n)return e;try{let r=new URL(n),i=new URL(e);return i.protocol=r.protocol,i.host=r.host,i.toString()}catch(r){return e}}getHmacSecret(){return this.config.environment==="staging"?this.brandConstants.hmacSecretStaging:this.brandConstants.hmacSecretProd}};I.LOCAL_HOSTNAMES=new Set(["localhost","127.0.0.1","::1"]);var C=I;var de={production:{apiUrl:"https://api.privateav.com",verifyUiUrl:"https://verify.privateav.com",engineUrl:"https://engine.privateav.com",wsUrl:"wss://engine.privateav.com/api/websocket/stream",trustedOrigins:["https://verify.privateav.com","https://portal.privateav.com","https://api.privateav.com"]},staging:{apiUrl:"https://api.verityav-staging-usw1a.privateav.com",verifyUiUrl:"https://verify.verityav-staging-usw1a.privateav.com",engineUrl:"https://engine.verityav-staging-usw1a.privateav.com",wsUrl:"wss://engine.verityav-staging-usw1a.privateav.com/api/websocket/stream",trustedOrigins:["https://verify.verityav-staging-usw1a.privateav.com","https://portal.verityav-staging-usw1a.privateav.com","https://api.verityav-staging-usw1a.privateav.com"]}},L={name:"PrivateAV",hmacSecretProd:"privateav-prod-hmac-2025",hmacSecretStaging:"privateav-stage-hmac-2025",messageType:"privateav:verification:complete",legacyMessageType:"privateav-verification",popupName:"privateav-verify",docsUrl:"https://docs.privateav.com"};var w=class extends C{constructor(e){super(e,de,L)}},ge="3.5.3";w.VERSION=ge;typeof window!="undefined"&&(j(),q(`${L.name} SDK`));var Pe=w;return ve(xe);})();
1
+ /* PrivateAV SDK v3.5.6 */
2
+ "use strict";var PrivateAVSDK=(()=>{var v=Object.defineProperty,Q=Object.defineProperties,ee=Object.getOwnPropertyDescriptor,te=Object.getOwnPropertyDescriptors,re=Object.getOwnPropertyNames,D=Object.getOwnPropertySymbols;var $=Object.prototype.hasOwnProperty,ie=Object.prototype.propertyIsEnumerable;var _=(r,e,t)=>e in r?v(r,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):r[e]=t,u=(r,e)=>{for(var t in e||(e={}))$.call(e,t)&&_(r,t,e[t]);if(D)for(var t of D(e))ie.call(e,t)&&_(r,t,e[t]);return r},N=(r,e)=>Q(r,te(e));var ne=(r,e)=>{for(var t in e)v(r,t,{get:e[t],enumerable:!0})},se=(r,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of re(e))!$.call(r,n)&&n!==t&&v(r,n,{get:()=>e[n],enumerable:!(i=ee(e,n))||i.enumerable});return r};var oe=r=>se(v({},"__esModule",{value:!0}),r);var he={};ne(he,{PrivateAV:()=>f,VERSION:()=>J,default:()=>fe});function k(){crypto.randomUUID||(crypto.randomUUID=function(){let r=new Uint8Array(16);crypto.getRandomValues(r),r[6]=r[6]&15|64,r[8]=r[8]&63|128;let e=Array.from(r).map(t=>t.toString(16).padStart(2,"0")).join("");return[e.slice(0,8),e.slice(8,12),e.slice(12,16),e.slice(16,20),e.slice(20,32)].join("-")})}function M(r="SDK"){let e=[];if(!window.crypto||!window.crypto.getRandomValues)throw new Error(`${r} requires Web Crypto API support`);crypto.randomUUID||e.push("crypto.randomUUID not supported, using polyfill"),window.URLSearchParams||e.push("URLSearchParams not supported, consider adding a polyfill for IE 11 support"),e.length>0&&console.warn(`${r} Browser Compatibility:`,e.join("; "))}function ae(r,e){return e.includes(r)}function K(r,e,t=[],i="SDK"){var s;let{origin:n}=r;return ae(n,e)||t.length>0&&t.some(l=>{if(l.startsWith("*.")){let c=l.slice(2);return n.endsWith(`.${c}`)||n===`https://${c}`||n===`http://${c}`}return n===l})?!0:(console.warn(`${i} Security: Blocked PostMessage from untrusted origin: ${n}`,{trustedOrigins:e,allowedCustomOrigins:t,eventType:(s=r.data)==null?void 0:s.type}),!1)}function B(r,e,t,i){let{data:n}=r;return!n||typeof n!="object"?{isValid:!1,error:"Invalid message format"}:(i?[t,i]:[t]).includes(n.type)?!n.sessionId||n.sessionId!==e?{isValid:!1,error:"Session ID mismatch"}:!n.status||!["verified","failed","cancelled"].includes(n.status)?{isValid:!1,error:"Invalid status value"}:{isValid:!0}:{isValid:!1,error:"Invalid message type"}}function W(r,e="SDK"){r==="production"&&window.location.protocol!=="https:"&&console.warn(`${e} Warning: HTTPS recommended for production environment`,{current:window.location.href})}function S(r,e,t="SDK"){try{let i=new URL(r);if(i.protocol==="file:")return{isValid:!1,error:"file:// URLs are not supported. The verification redirect cannot return to local files. Please use a local web server (e.g., npx serve . or python3 -m http.server) instead of opening the HTML file directly."};if(i.protocol!=="https:"&&!(i.hostname==="localhost"||i.hostname==="127.0.0.1"))return{isValid:!1,error:`HTTPS required for return URLs in ${e}`};let n=[/data:/i,/javascript:/i,/vbscript:/i,/file:/i,/ftp:/i];for(let s of n)if(s.test(r))return{isValid:!1,error:"Blocked suspicious URL scheme"};return{isValid:!0}}catch(i){return{isValid:!1,error:"Invalid URL format"}}}var E=class{constructor(){this.attempts=new Map;this.maxAttempts=5;this.timeWindow=6e4}isAllowed(e,t="SDK"){let i=Date.now(),s=(this.attempts.get(e)||[]).filter(o=>i-o<this.timeWindow);return s.length>=this.maxAttempts?(console.warn(`${t} Security: Rate limit exceeded for ${e}`),!1):(s.push(i),this.attempts.set(e,s),!0)}reset(e){this.attempts.delete(e)}},F=new E;function d(r,e,t="SDK"){let i={timestamp:new Date().toISOString(),userAgent:typeof navigator!="undefined"?navigator.userAgent:"unknown",url:typeof window!="undefined"?window.location.href:"unknown"};console.warn(`${t} Security Event: ${r}`,u(u({},i),e))}var H=25,q=150,w=2048,j=128,le=/^(pk_|sk_)[a-zA-Z0-9_]+$/;function G(r,e){var n;if(!r.apiKey)throw new Error("apiKey is required");if(r.apiKey.length>j)throw new Error(`apiKey exceeds maximum length of ${j} characters`);if(!le.test(r.apiKey))throw new Error("Invalid apiKey format. Expected pk_xxx (public) or sk_xxx (private)");if(typeof window!="undefined"&&r.apiKey.startsWith("sk_"))throw new Error(`Secret keys (sk_) should never be used in browser code for security reasons. Secret keys expose your account to unauthorized access if used client-side. Please use your public key (pk_) instead. If you need to use features that require a secret key (like custom challenge age), create the session server-side and pass the sessionId to startVerificationWithSession(). See: ${e.docsUrl}/server-side-sessions`);if(!r.returnUrl)throw new Error("returnUrl is required");if(r.returnUrl.length>w)throw new Error(`returnUrl exceeds maximum length of ${w} characters`);let t=(n=e.environment)!=null?n:"production",i=S(r.returnUrl,t,e.brandName);if(!i.isValid)throw new Error(`returnUrl validation failed: ${i.error}`);if(r.cancelUrl){if(r.cancelUrl.length>w)throw new Error(`cancelUrl exceeds maximum length of ${w} characters`);let s=S(r.cancelUrl,t,e.brandName);if(!s.isValid)throw new Error(`cancelUrl validation failed: ${s.error}`)}if(r.defaultChallengeAge!==void 0){if(r.defaultChallengeAge<H)throw new Error(`defaultChallengeAge must be at least ${H}`);if(r.defaultChallengeAge>q)throw new Error(`defaultChallengeAge cannot exceed ${q}`)}if(r.defaultVerificationMode&&!["L1","L2"].includes(r.defaultVerificationMode))throw new Error("defaultVerificationMode must be L1 or L2");if(r.faceMatchEnabled!==void 0&&typeof r.faceMatchEnabled!="boolean")throw new Error("faceMatchEnabled must be a boolean");if(r.mode&&!["redirect","new-tab"].includes(r.mode))throw new Error("mode must be redirect or new-tab");if(r.newTabTarget&&!["popup","tab"].includes(r.newTabTarget))throw new Error("newTabTarget must be popup or tab")}function b(r,e){let t=e.verifyUiUrl;if(!t||!t.startsWith("https://"))throw new Error(`HTTPS required for ${r} environment`);return t}function ce(r,e){let t=e.apiUrl;if(!t||!t.startsWith("https://"))throw new Error(`HTTPS required for API URLs in ${r} environment`);return t}function de(r){if(!r)return null;try{return new URL(r).hostname.toLowerCase()}catch(e){return null}}function ge(r){let e=r.split(".");return e.length<=2?r:e.slice(1).join(".")}function Z(r){let e=new Set;for(let t of r){let i=de(t);i&&e.add(ge(i))}return e}function pe(r){var s,o;if(!r||!r.staging)return[];let e=r.staging,t=Z([e.apiUrl,e.verifyUiUrl,e.engineUrl,e.wsUrl,...(s=e.trustedOrigins)!=null?s:[]]),i=r.production,n=i?Z([i.apiUrl,i.verifyUiUrl,i.engineUrl,i.wsUrl,...(o=i.trustedOrigins)!=null?o:[]]):new Set;for(let l of n)t.delete(l);return Array.from(t)}function ue(r,e){let t=(r||"").toLowerCase();return t?pe(e).some(i=>t===i||t.endsWith(`.${i}`)):!1}function X(r,e){return r==="staging"||r==="production"?r:r||typeof window=="undefined"||!window.location?"production":ue(window.location.hostname,e)?"staging":"production"}function z(r,e,t="SDK"){let i=window.location.protocol==="https:";switch(r){case"production":i||console.warn(`${t} Warning: HTTPS recommended for production environment`);break;case"staging":i||console.warn(`${t} Warning: HTTPS strongly recommended in staging environment`);break}try{b(r,e),ce(r,e)}catch(n){let s=n instanceof Error?n.message:String(n);throw new Error(`Environment configuration validation failed: ${s}`)}}var y=class y{constructor(e,t,i){this.popupWindow=null;this.messageListener=null;this.popupMonitorInterval=null;this.unloadListener=null;this.isVerificationInProgress=!1;this.currentSessionId=null;this.hasReceivedResult=!1;this.lastVerifyUrl=null;this.lastExternalUserId=null;this.brandUrls=t,this.brandConstants=i;let n=X(e.environment,this.brandUrls);e.environment&&e.environment!=="staging"&&e.environment!=="production"&&console.warn(`${this.brandConstants.name} SDK: Unknown environment '${e.environment}', defaulting to 'production'`),G(e,{brandName:this.brandConstants.name,docsUrl:this.brandConstants.docsUrl,environment:n}),this.config=N(u({},e),{environment:n,mode:e.mode||"redirect",newTabTarget:e.newTabTarget||"popup"}),this.validateServiceOverrides(),z(this.config.environment,this.getUrlConfig(),this.brandConstants.name),W(this.config.environment,this.brandConstants.name),d("SDK_INITIALIZED",{environment:this.config.environment,mode:this.config.mode,origin:window.location.origin,protocol:window.location.protocol,hostname:window.location.hostname},this.brandConstants.name),this.setupAutoCleanup()}async verify(e={}){var n,s,o,l,c,g;let t=this.isPublicKey(),i;if(t)i=await this.createInternalSession(e);else throw new Error("Private API keys (sk_) should use the direct API, not the SDK. The SDK is designed for browser-based public key usage only.");if(!i)throw new Error("Failed to obtain sessionId from server");if(this.isVerificationInProgress){let a=new Error(`Verification already in progress for session ${(n=this.currentSessionId)==null?void 0:n.substring(0,8)}...`);throw d("RACE_CONDITION_PREVENTED",{currentSession:((s=this.currentSessionId)==null?void 0:s.substring(0,8))+"...",attemptedSession:"new-session-attempt",origin:window.location.origin},this.brandConstants.name),(l=(o=this.config).onError)==null||l.call(o,a),a}this.isVerificationInProgress=!0,this.currentSessionId=i,this.lastExternalUserId=e.externalUserId||null;try{let a=`${this.config.apiKey}:${window.location.origin}`;if(!F.isAllowed(a,this.brandConstants.name)){let h=new Error("Too many verification attempts. Please wait before trying again.");throw d("RATE_LIMIT_EXCEEDED",{apiKey:this.config.apiKey.substring(0,8)+"...",origin:window.location.origin,sessionId:i?i.substring(0,8)+"...":"undefined"},this.brandConstants.name),(g=(c=this.config).onError)==null||g.call(c,h),h}let p=await this.buildVerificationUrl(e,i);d("VERIFICATION_INITIATED",{environment:this.config.environment,mode:this.config.mode,sessionId:i?i.substring(0,8)+"...":"undefined",origin:window.location.origin},this.brandConstants.name),this.config.mode==="new-tab"?this.openNewTab(p,i):(this.unlockVerification(),this.redirect(p))}catch(a){throw this.unlockVerification(),a}}async buildVerificationUrl(e,t){if(!this.lastVerifyUrl)throw new Error("Server did not return an authoritative verification URL");let i=e.language||this.config.language,n=new URL(this.lastVerifyUrl),s=new URL(this.getUrlConfig().verifyUiUrl).origin;if(n.origin!==s)throw new Error("Server returned an untrusted verification origin");if(n.searchParams.get("sessionId")!==t||!n.searchParams.get("sessionToken"))throw new Error("Server verification URL is missing authoritative session context");let o=new URL(this.applyLocalVerifyOverride(n.toString()));return o.searchParams.set("mode",this.config.mode),e.skipIntro&&o.searchParams.set("skip_intro","true"),e.autoReturn&&o.searchParams.set("auto_return","true"),i&&o.searchParams.set("lang",i),o.toString()}redirect(e){window.location.href=e}openNewTab(e,t){var c,g;if(this.cleanup(),this.hasReceivedResult=!1,this.popupMonitorInterval&&(clearInterval(this.popupMonitorInterval),this.popupMonitorInterval=null),(this.config.newTabTarget||"popup")==="tab"?this.popupWindow=window.open(e,"_blank"):this.popupWindow=window.open(e,this.brandConstants.popupName,"width=600,height=700"),!this.popupWindow){(g=(c=this.config).onError)==null||g.call(c,new Error("Failed to open verification window. Please check popup blocker settings."));return}let n=this.getTrustedOrigins(),s=this.getAllowedCustomOrigins(e),o=this.brandConstants.messageType,l=this.brandConstants.legacyMessageType;this.messageListener=a=>{var L,A,R,O,P,V,x;let p=(L=a.data)==null?void 0:L.type;if(!p||typeof p!="string"||!(l?[o,l]:[o]).includes(p))return;if(!K(a,n,s,this.brandConstants.name)){d("POSTMESSAGE_ORIGIN_BLOCKED",{origin:a.origin,environment:this.config.environment,expectedOrigins:`${this.brandConstants.name} trusted origins for ${this.config.environment}`,messageType:(A=a.data)==null?void 0:A.type},this.brandConstants.name);return}let I=B(a,t,o,l);if(!I.isValid){d("POSTMESSAGE_VALIDATION_FAILED",{error:I.error,origin:a.origin,sessionId:t.substring(0,8)+"...",messageType:(R=a.data)==null?void 0:R.type},this.brandConstants.name);return}let T=a.data.status;if(T==="cancelled"){this.handleCancellation(t,"postmessage");return}let m={sessionId:a.data.sessionId,status:T,timestamp:a.data.timestamp,externalUserId:a.data.externalUserId};this.hasReceivedResult=!0,d("VERIFICATION_COMPLETED",{status:m.status,sessionId:t.substring(0,8)+"...",origin:a.origin},this.brandConstants.name),this.cleanup({closePopup:!1}),this.unlockVerification(),this.popupMonitorInterval&&(clearInterval(this.popupMonitorInterval),this.popupMonitorInterval=null),m.status==="verified"?(P=(O=this.config).onComplete)==null||P.call(O,m):(x=(V=this.config).onError)==null||x.call(V,new Error(`Verification failed: ${m.status}`))},window.addEventListener("message",this.messageListener),this.popupMonitorInterval=setInterval(()=>{this.popupWindow&&this.popupWindow.closed&&(d("POPUP_CLOSED_BY_USER",{sessionId:t.substring(0,8)+"...",environment:this.config.environment},this.brandConstants.name),this.hasReceivedResult||this.handleCancellation(t,"popup-closed"))},500)}setupAutoCleanup(){if(this.unloadListener=()=>{d("SDK_AUTO_CLEANUP",{environment:this.config.environment,trigger:"page_unload"},this.brandConstants.name),this.cleanup(),this.unlockVerification()},window.addEventListener("beforeunload",this.unloadListener),window.addEventListener("pagehide",this.unloadListener),window.history&&window.history.pushState){let e=window.history.pushState;window.history.pushState=(...t)=>(this.cleanup(),this.unlockVerification(),e.apply(window.history,t))}}getEnvironment(){return this.config.environment}unlockVerification(){this.isVerificationInProgress=!1,this.currentSessionId=null,d("VERIFICATION_UNLOCKED",{environment:this.config.environment,origin:window.location.origin},this.brandConstants.name)}cleanup(e={}){let t=e.closePopup!==!1;this.popupWindow&&(t&&!this.popupWindow.closed&&this.popupWindow.close(),(t||this.popupWindow.closed)&&(this.popupWindow=null)),this.messageListener&&(window.removeEventListener("message",this.messageListener),this.messageListener=null),this.popupMonitorInterval&&(clearInterval(this.popupMonitorInterval),this.popupMonitorInterval=null)}handleCancellation(e,t){if(this.hasReceivedResult)return;this.hasReceivedResult=!0,d("VERIFICATION_CANCELLED",{source:t,sessionId:e.substring(0,8)+"...",environment:this.config.environment,origin:window.location.origin},this.brandConstants.name),this.cleanup(),this.unlockVerification();let i=!0;if(this.config.onCancel)try{this.config.onCancel()===!1&&(i=!1)}catch(n){d("CANCEL_CALLBACK_FAILED",{error:n instanceof Error?n.message:String(n),sessionId:e.substring(0,8)+"..."},this.brandConstants.name)}i&&this.redirectToCancelUrl(e)}redirectToCancelUrl(e){if(this.config.cancelUrl)try{let t=decodeURIComponent(this.config.cancelUrl),i=new URL(t);i.searchParams.set("sessionId",e),i.searchParams.set("status","cancelled"),i.searchParams.set("timestamp",Date.now().toString()),this.lastExternalUserId&&i.searchParams.set("externalUserId",this.lastExternalUserId),window.location.href=i.toString()}catch(t){d("CANCEL_REDIRECT_FAILED",{error:t instanceof Error?t.message:String(t),sessionId:e.substring(0,8)+"..."},this.brandConstants.name)}}removeAutoCleanupListeners(){this.unloadListener&&(window.removeEventListener("beforeunload",this.unloadListener),window.removeEventListener("pagehide",this.unloadListener),this.unloadListener=null)}destroy(){d("SDK_DESTROYED",{environment:this.config.environment,origin:window.location.origin},this.brandConstants.name),this.cleanup(),this.unlockVerification(),this.removeAutoCleanupListeners()}getPortalApiUrl(){let e=this.getUrlConfig().apiUrl;if(!this.config.apiUrl)return e;let t=this.getUrlOrigin(this.config.apiUrl),i=this.getUrlOrigin(e);if(t===i||this.getLocalOrigin(this.config.apiUrl))return this.config.apiUrl;throw new Error("apiUrl must use the selected brand/environment service")}validateServiceOverrides(){let e=this.getUrlConfig();if(this.config.apiUrl){let t=this.getUrlOrigin(this.config.apiUrl),i=this.getUrlOrigin(e.apiUrl);if(t!==i&&!this.getLocalOrigin(this.config.apiUrl))throw new Error("apiUrl must use the selected brand/environment service")}if(this.config.verifyUrl){let t=this.getUrlOrigin(this.config.verifyUrl),i=this.getUrlOrigin(e.verifyUiUrl);if(t!==i&&!this.getLocalOrigin(this.config.verifyUrl))throw new Error("verifyUrl must use the selected brand/environment service")}}isPublicKey(){return this.config.apiKey.startsWith("pk_")}async createInternalSession(e){var t,i;try{let n=this.getPortalApiUrl(),s=await fetch(`${n}/api/v1/sessions/create`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.config.apiKey}`},body:JSON.stringify({merchantId:this.config.apiKey,returnUrl:this.config.returnUrl,cancelUrl:this.config.cancelUrl,merchantName:document.title||window.location.hostname,externalUserId:e.externalUserId})});if(!s.ok){let c=await s.json().catch(()=>({})),g=c==null?void 0:c.code;if(this.isBillingBlockError(g)){let a=e.language||this.config.language;this.openBillingBlockPage(g,a)}throw new Error(`Failed to create session: ${s.status} ${s.statusText}. ${c.message||""}`)}let o=await s.json(),l=o.sessionId;if(!l)throw new Error("Server did not return a sessionId");return o.verifyUrl&&(this.lastVerifyUrl=o.verifyUrl),d("INTERNAL_SESSION_CREATED",{sessionId:l.substring(0,8)+"...",environment:this.config.environment,apiKeyType:"public"},this.brandConstants.name),l}catch(n){let s=n instanceof Error?n.message:String(n);throw d("INTERNAL_SESSION_FAILED",{error:s,environment:this.config.environment,apiKeyType:"public"},this.brandConstants.name),(i=(t=this.config).onError)==null||i.call(t,n),new Error(`Failed to create verification session: ${s}`)}}isBillingBlockError(e){return e==="SUBSCRIPTION_REQUIRED"||e==="PLAN_LIMIT_REACHED"||e==="SANDBOX_LIMIT_REACHED"}openBillingBlockPage(e,t){var i,n,s,o;try{let l=b(this.config.environment,this.getUrlConfig()),c=this.applyLocalVerifyOverride(l),g=new URL(c);g.searchParams.set("blocked",e);let a=t||this.config.language;if(a&&g.searchParams.set("lang",a),this.config.mode==="new-tab"){((this.config.newTabTarget||"popup")==="tab"?window.open(g.toString(),"_blank"):window.open(g.toString(),this.brandConstants.popupName,"width=600,height=700"))||(n=(i=this.config).onError)==null||n.call(i,new Error("Failed to open billing notice window. Please check popup blocker settings."));return}this.redirect(g.toString())}catch(l){let c=l instanceof Error?l.message:String(l);(o=(s=this.config).onError)==null||o.call(s,new Error(`Failed to open billing notice: ${c}`))}}getUrlConfig(){return this.brandUrls[this.config.environment]||this.brandUrls.production}getTrustedOrigins(){return this.getUrlConfig().trustedOrigins}getAllowedCustomOrigins(e){let t=new Set,i=this.getLocalOrigin(this.config.verifyUrl||null),n=this.getLocalOrigin(e||null);return i&&t.add(i),n&&t.add(n),Array.from(t)}getLocalOrigin(e){if(!e)return null;try{let t=new URL(e);if(y.LOCAL_HOSTNAMES.has(t.hostname))return t.origin}catch(t){return null}return null}getUrlOrigin(e){try{return new URL(e).origin}catch(t){return null}}applyLocalVerifyOverride(e){let t=this.getLocalOrigin(this.config.verifyUrl||null);if(!t)return e;try{let i=new URL(t),n=new URL(e);return n.protocol=i.protocol,n.host=i.host,n.toString()}catch(i){return e}}};y.LOCAL_HOSTNAMES=new Set(["localhost","127.0.0.1","::1"]);var U=y;var Y={production:{apiUrl:"https://api.privateav.com",verifyUiUrl:"https://verify.privateav.com",engineUrl:"https://engine.privateav.com",wsUrl:"wss://engine.privateav.com/api/websocket/stream",trustedOrigins:["https://verify.privateav.com"]},staging:{apiUrl:"https://api.staging.privateav.com",verifyUiUrl:"https://verify.staging.privateav.com",engineUrl:"https://engine.staging.privateav.com",wsUrl:"wss://engine.staging.privateav.com/api/websocket/stream",trustedOrigins:["https://verify.staging.privateav.com"]}},C={name:"PrivateAV",messageType:"privateav:verification:complete",legacyMessageType:"privateav-verification",popupName:"privateav-verify",docsUrl:"https://docs.privateav.com"};var f=class extends U{constructor(e){super(e,Y,C)}},J="3.5.6";f.VERSION=J;typeof window!="undefined"&&(k(),M(`${C.name} SDK`));var fe=f;return oe(he);})();
3
3
  if(typeof PrivateAVSDK !== "undefined" && PrivateAVSDK.PrivateAV) { window.PrivateAV = PrivateAVSDK.PrivateAV; window.PrivateAV.VERSION = PrivateAVSDK.VERSION; }
package/sdk.min.js CHANGED
@@ -1,3 +1,3 @@
1
- /* PrivateAV SDK v3.5.3 */
2
- "use strict";var PrivateAVSDK=(()=>{var S=Object.defineProperty,pe=Object.defineProperties,ue=Object.getOwnPropertyDescriptor,fe=Object.getOwnPropertyDescriptors,he=Object.getOwnPropertyNames,y=Object.getOwnPropertySymbols;var T=Object.prototype.hasOwnProperty,W=Object.prototype.propertyIsEnumerable;var B=(t,e,n)=>e in t?S(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n,v=(t,e)=>{for(var n in e||(e={}))T.call(e,n)&&B(t,n,e[n]);if(y)for(var n of y(e))W.call(e,n)&&B(t,n,e[n]);return t},U=(t,e)=>pe(t,fe(e));var H=(t,e)=>{var n={};for(var r in t)T.call(t,r)&&e.indexOf(r)<0&&(n[r]=t[r]);if(t!=null&&y)for(var r of y(t))e.indexOf(r)<0&&W.call(t,r)&&(n[r]=t[r]);return n};var A=(t,e)=>()=>(t&&(e=t(t=0)),e);var F=(t,e)=>{for(var n in e)S(t,n,{get:e[n],enumerable:!0})},me=(t,e,n,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of he(e))!T.call(t,i)&&i!==n&&S(t,i,{get:()=>e[i],enumerable:!(r=ue(e,i))||r.enumerable});return t};var ve=t=>me(S({},"__esModule",{value:!0}),t);function we(t,e){return e.includes(t)}function G(t,e,n=[],r="SDK"){var s;let{origin:i}=t;return we(i,e)||n.length>0&&n.some(a=>{if(a.startsWith("*.")){let c=a.slice(2);return i.endsWith(`.${c}`)||i===`https://${c}`||i===`http://${c}`}return i===a})?!0:(console.warn(`${r} Security: Blocked PostMessage from untrusted origin: ${i}`,{trustedOrigins:e,allowedCustomOrigins:n,eventType:(s=t.data)==null?void 0:s.type}),!1)}function J(t,e,n,r){let{data:i}=t;return!i||typeof i!="object"?{isValid:!1,error:"Invalid message format"}:(r?[n,r]:[n]).includes(i.type)?!i.sessionId||i.sessionId!==e?{isValid:!1,error:"Session ID mismatch"}:!i.status||!["verified","failed","cancelled"].includes(i.status)?{isValid:!1,error:"Invalid status value"}:{isValid:!0}:{isValid:!1,error:"Invalid message type"}}function X(t,e="SDK"){t==="production"&&window.location.protocol!=="https:"&&console.warn(`${e} Warning: HTTPS recommended for production environment`,{current:window.location.href})}function x(t,e,n="SDK"){try{let r=new URL(t);if(r.protocol==="file:")return{isValid:!1,error:"file:// URLs are not supported. The verification redirect cannot return to local files. Please use a local web server (e.g., npx serve . or python3 -m http.server) instead of opening the HTML file directly."};if(r.protocol!=="https:"&&!(r.hostname==="localhost"||r.hostname==="127.0.0.1"))return{isValid:!1,error:`HTTPS required for return URLs in ${e}`};let i=[/data:/i,/javascript:/i,/vbscript:/i,/file:/i,/ftp:/i];for(let s of i)if(s.test(t))return{isValid:!1,error:"Blocked suspicious URL scheme"};return{isValid:!0}}catch(r){return{isValid:!1,error:"Invalid URL format"}}}function p(t,e,n="SDK"){let r={timestamp:new Date().toISOString(),userAgent:typeof navigator!="undefined"?navigator.userAgent:"unknown",url:typeof window!="undefined"?window.location.href:"unknown"};console.warn(`${n} Security Event: ${t}`,v(v({},r),e))}var P,Y,R=A(()=>{"use strict";P=class{constructor(){this.attempts=new Map;this.maxAttempts=5;this.timeWindow=6e4}isAllowed(e,n="SDK"){let r=Date.now(),s=(this.attempts.get(e)||[]).filter(l=>r-l<this.timeWindow);return s.length>=this.maxAttempts?(console.warn(`${n} Security: Rate limit exceeded for ${e}`),!1):(s.push(r),this.attempts.set(e,s),!0)}reset(e){this.attempts.delete(e)}},Y=new P});var Q={};F(Q,{createSignedState:()=>Se,generateHMAC:()=>M,generateSecureToken:()=>z,parseSignedState:()=>Ue,verifyHMAC:()=>Z});async function M(t,e){let n=new TextEncoder,r=n.encode(e),i=n.encode(t),s=await crypto.subtle.importKey("raw",r,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),l=await crypto.subtle.sign("HMAC",s,i);return Array.from(new Uint8Array(l)).map(a=>a.toString(16).padStart(2,"0")).join("")}async function Z(t,e,n){try{let r=await M(t,n);return ye(e,r)}catch(r){return!1}}function ye(t,e){if(t.length!==e.length)return!1;let n=0;for(let r=0;r<t.length;r++)n|=t.charCodeAt(r)^e.charCodeAt(r);return n===0}function z(t=32){let e=new Uint8Array(t);return crypto.getRandomValues(e),Array.from(e,n=>n.toString(16).padStart(2,"0")).join("")}async function Se(t,e){let n=U(v({},t),{timestamp:Date.now(),nonce:z(16)}),r=JSON.stringify(n),i=await M(r,e);return btoa(JSON.stringify({data:n,signature:i}))}async function Ue(t,e,n=te,r="SDK"){try{let s=atob(t),l=JSON.parse(s);if(!l.data||!l.signature)return console.warn(`${r}: Invalid signed state format`),null;let{data:a,signature:c}=l,d=JSON.stringify(a);if(!await Z(d,c,e))return console.warn(`${r}: State signature verification failed`),null;if(a.timestamp){let h=Date.now()-a.timestamp;if(h>n)return console.warn(`${r}: State parameter expired`,{age:h,maxAge:n}),null}let i=a,{timestamp:g,nonce:f}=i;return H(i,["timestamp","nonce"])}catch(s){return console.warn(`${r}: Failed to parse signed state`,s),null}}var ee=A(()=>{"use strict";V()});function se(t,e){var i;if(!t.apiKey)throw new Error("apiKey is required");if(t.apiKey.length>ie)throw new Error(`apiKey exceeds maximum length of ${ie} characters`);if(!be.test(t.apiKey))throw new Error("Invalid apiKey format. Expected pk_xxx (public) or sk_xxx (private)");if(typeof window!="undefined"&&t.apiKey.startsWith("sk_"))throw new Error(`Secret keys (sk_) should never be used in browser code for security reasons. Secret keys expose your account to unauthorized access if used client-side. Please use your public key (pk_) instead. If you need to use features that require a secret key (like custom challenge age), create the session server-side and pass the sessionId to startVerificationWithSession(). See: ${e.docsUrl}/server-side-sessions`);if(!t.returnUrl)throw new Error("returnUrl is required");if(t.returnUrl.length>b)throw new Error(`returnUrl exceeds maximum length of ${b} characters`);let n=(i=e.environment)!=null?i:"production",r=x(t.returnUrl,n,e.brandName);if(!r.isValid)throw new Error(`returnUrl validation failed: ${r.error}`);if(t.cancelUrl){if(t.cancelUrl.length>b)throw new Error(`cancelUrl exceeds maximum length of ${b} characters`);let s=x(t.cancelUrl,n,e.brandName);if(!s.isValid)throw new Error(`cancelUrl validation failed: ${s.error}`)}if(t.defaultChallengeAge!==void 0){if(t.defaultChallengeAge<ne)throw new Error(`defaultChallengeAge must be at least ${ne}`);if(t.defaultChallengeAge>re)throw new Error(`defaultChallengeAge cannot exceed ${re}`)}if(t.defaultVerificationMode&&!["L1","L2"].includes(t.defaultVerificationMode))throw new Error("defaultVerificationMode must be L1 or L2");if(t.faceMatchEnabled!==void 0&&typeof t.faceMatchEnabled!="boolean")throw new Error("faceMatchEnabled must be a boolean");if(t.mode&&!["redirect","new-tab"].includes(t.mode))throw new Error("mode must be redirect or new-tab");if(t.newTabTarget&&!["popup","tab"].includes(t.newTabTarget))throw new Error("newTabTarget must be popup or tab")}async function oe(t,e,n,r="SDK"){let{createSignedState:i}=await Promise.resolve().then(()=>(ee(),Q));return i(t,n)}var ne,re,b,ie,te,be,V=A(()=>{"use strict";R();ne=25,re=150,b=2048,ie=128,te=6e5,be=/^(pk_|sk_)[a-zA-Z0-9_]+$/});var xe={};F(xe,{PrivateAV:()=>w,VERSION:()=>ge,default:()=>Pe});function j(){crypto.randomUUID||(crypto.randomUUID=function(){let t=new Uint8Array(16);crypto.getRandomValues(t),t[6]=t[6]&15|64,t[8]=t[8]&63|128;let e=Array.from(t).map(n=>n.toString(16).padStart(2,"0")).join("");return[e.slice(0,8),e.slice(8,12),e.slice(12,16),e.slice(16,20),e.slice(20,32)].join("-")})}function q(t="SDK"){let e=[];if(!window.crypto||!window.crypto.getRandomValues)throw new Error(`${t} requires Web Crypto API support`);if(!window.crypto.subtle)throw new Error(`${t} requires Web Crypto subtle API for HMAC operations`);crypto.randomUUID||e.push("crypto.randomUUID not supported, using polyfill"),window.URLSearchParams||e.push("URLSearchParams not supported, consider adding a polyfill for IE 11 support"),e.length>0&&console.warn(`${t} Browser Compatibility:`,e.join("; "))}V();function E(t,e){let n=e.verifyUiUrl;if(!n||!n.startsWith("https://"))throw new Error(`HTTPS required for ${t} environment`);return n}function Ee(t,e){let n=e.apiUrl;if(!n||!n.startsWith("https://"))throw new Error(`HTTPS required for API URLs in ${t} environment`);return n}function Ce(t){if(!t)return null;try{return new URL(t).hostname.toLowerCase()}catch(e){return null}}function Ie(t){let e=t.split(".");return e.length<=2?t:e.slice(1).join(".")}function ae(t){let e=new Set;for(let n of t){let r=Ce(n);r&&e.add(Ie(r))}return e}function Te(t){var s,l;if(!t||!t.staging)return[];let e=t.staging,n=ae([e.apiUrl,e.verifyUiUrl,e.engineUrl,e.wsUrl,...(s=e.trustedOrigins)!=null?s:[]]),r=t.production,i=r?ae([r.apiUrl,r.verifyUiUrl,r.engineUrl,r.wsUrl,...(l=r.trustedOrigins)!=null?l:[]]):new Set;for(let a of i)n.delete(a);return Array.from(n)}function Ae(t,e){let n=(t||"").toLowerCase();return n?Te(e).some(r=>n===r||n.endsWith(`.${r}`)):!1}function le(t,e){return t==="staging"||t==="production"?t:t||typeof window=="undefined"||!window.location?"production":Ae(window.location.hostname,e)?"staging":"production"}function ce(t,e,n="SDK"){let r=window.location.protocol==="https:";switch(t){case"production":r||console.warn(`${n} Warning: HTTPS recommended for production environment`);break;case"staging":r||console.warn(`${n} Warning: HTTPS strongly recommended in staging environment`);break}try{E(t,e),Ee(t,e)}catch(i){let s=i instanceof Error?i.message:String(i);throw new Error(`Environment configuration validation failed: ${s}`)}}R();var I=class I{constructor(e,n,r){this.popupWindow=null;this.messageListener=null;this.popupMonitorInterval=null;this.unloadListener=null;this.isVerificationInProgress=!1;this.currentSessionId=null;this.hasReceivedResult=!1;this.lastVerifyUrl=null;this.lastSessionToken=null;this.lastExternalUserId=null;this.lastSandboxMode=null;this.temporaryHandoffToken=null;this.brandUrls=n,this.brandConstants=r;let i=le(e.environment,this.brandUrls);e.environment&&e.environment!=="staging"&&e.environment!=="production"&&console.warn(`${this.brandConstants.name} SDK: Unknown environment '${e.environment}', defaulting to 'production'`),se(e,{brandName:this.brandConstants.name,docsUrl:this.brandConstants.docsUrl,environment:i}),this.config=U(v({},e),{environment:i,mode:e.mode||"redirect",newTabTarget:e.newTabTarget||"popup"}),ce(this.config.environment,this.getUrlConfig(),this.brandConstants.name),X(this.config.environment,this.brandConstants.name),p("SDK_INITIALIZED",{environment:this.config.environment,mode:this.config.mode,origin:window.location.origin,protocol:window.location.protocol,hostname:window.location.hostname},this.brandConstants.name),this.setupAutoCleanup()}async verify(e={}){var i,s,l,a,c,d;let n=this.isPublicKey(),r;if(n)r=await this.createInternalSession(e);else throw new Error("Private API keys (sk_) should use the direct API, not the SDK. The SDK is designed for browser-based public key usage only.");if(!r)throw new Error("Failed to obtain sessionId from server");if(this.isVerificationInProgress){let o=new Error(`Verification already in progress for session ${(i=this.currentSessionId)==null?void 0:i.substring(0,8)}...`);throw p("RACE_CONDITION_PREVENTED",{currentSession:((s=this.currentSessionId)==null?void 0:s.substring(0,8))+"...",attemptedSession:"new-session-attempt",origin:window.location.origin},this.brandConstants.name),(a=(l=this.config).onError)==null||a.call(l,o),o}this.isVerificationInProgress=!0,this.currentSessionId=r,this.lastExternalUserId=e.externalUserId||null;try{let o=`${this.config.apiKey}:${window.location.origin}`;if(!Y.isAllowed(o,this.brandConstants.name)){let f=new Error("Too many verification attempts. Please wait before trying again.");throw p("RATE_LIMIT_EXCEEDED",{apiKey:this.config.apiKey.substring(0,8)+"...",origin:window.location.origin,sessionId:r?r.substring(0,8)+"...":"undefined"},this.brandConstants.name),(d=(c=this.config).onError)==null||d.call(c,f),f}let g=await this.buildVerificationUrl(e,r);p("VERIFICATION_INITIATED",{environment:this.config.environment,mode:this.config.mode,sessionId:r?r.substring(0,8)+"...":"undefined",origin:window.location.origin},this.brandConstants.name),this.config.mode==="new-tab"?this.openNewTab(g,r):(this.unlockVerification(),this.redirect(g))}catch(o){throw this.unlockVerification(),o}}async buildVerificationUrl(e,n){var f,m;let r=this.config.verifyUrl||E(this.config.environment,this.getUrlConfig()),i=e.challengeAge!==void 0,s=e.verificationMode!==void 0,l=e.faceMatchEnabled!==void 0||this.config.faceMatchEnabled!==void 0,a=i||s||l,c=(f=e.faceMatchEnabled)!=null?f:this.config.faceMatchEnabled,d=await oe({merchantId:this.config.apiKey,sessionId:n,returnUrl:this.config.returnUrl,cancelUrl:this.config.cancelUrl,challengeAge:e.challengeAge||this.config.defaultChallengeAge,verificationMode:e.verificationMode||this.config.defaultVerificationMode,faceMatchEnabled:c,hasOverrides:a,externalUserId:e.externalUserId,timestamp:Date.now(),apiUrl:this.getPortalApiUrl(),engineUrl:this.getEngineUrl(),wsUrl:this.getWebSocketUrl(),environment:this.config.environment,features:{testMode:!1,warmupPeriodMs:500,qualityThreshold:.6,sandboxMode:(m=this.lastSandboxMode)!=null?m:!1},handoffToken:this.temporaryHandoffToken||void 0,sessionToken:this.lastSessionToken||void 0,verifyUrl:this.lastVerifyUrl||void 0},this.config.environment,this.getHmacSecret(),this.brandConstants.name),o=e.language||this.config.language;if(this.lastVerifyUrl)try{let h=this.applyLocalVerifyOverride(this.lastVerifyUrl),u=new URL(h);return u.searchParams.set("state",d),u.searchParams.set("mode",this.config.mode),e.skipIntro&&u.searchParams.set("skip_intro","true"),e.autoReturn&&u.searchParams.set("auto_return","true"),o&&u.searchParams.set("lang",o),u.toString()}catch(h){}let g=new URLSearchParams({state:d,sessionId:n,mode:this.config.mode});return e.skipIntro&&g.set("skip_intro","true"),e.autoReturn&&g.set("auto_return","true"),o&&g.set("lang",o),`${r}/?${g.toString()}`}redirect(e){window.location.href=e}openNewTab(e,n){var c,d;if(this.cleanup(),this.hasReceivedResult=!1,this.popupMonitorInterval&&(clearInterval(this.popupMonitorInterval),this.popupMonitorInterval=null),(this.config.newTabTarget||"popup")==="tab"?this.popupWindow=window.open(e,"_blank"):this.popupWindow=window.open(e,this.brandConstants.popupName,"width=600,height=700"),!this.popupWindow){(d=(c=this.config).onError)==null||d.call(c,new Error("Failed to open verification window. Please check popup blocker settings."));return}let i=this.getTrustedOrigins(),s=this.getAllowedCustomOrigins(e),l=this.brandConstants.messageType,a=this.brandConstants.legacyMessageType;this.messageListener=o=>{var k,D,_,O,$,N,K;let g=(k=o.data)==null?void 0:k.type;if(!g||typeof g!="string"||!(a?[l,a]:[l]).includes(g))return;if(!G(o,i,s,this.brandConstants.name)){p("POSTMESSAGE_ORIGIN_BLOCKED",{origin:o.origin,environment:this.config.environment,expectedOrigins:`${this.brandConstants.name} trusted origins for ${this.config.environment}`,messageType:(D=o.data)==null?void 0:D.type},this.brandConstants.name);return}let m=J(o,n,l,a);if(!m.isValid){p("POSTMESSAGE_VALIDATION_FAILED",{error:m.error,origin:o.origin,sessionId:n.substring(0,8)+"...",messageType:(_=o.data)==null?void 0:_.type},this.brandConstants.name);return}let h=o.data.status;if(h==="cancelled"){this.handleCancellation(n,"postmessage");return}let u={sessionId:o.data.sessionId,status:h,timestamp:o.data.timestamp,externalUserId:o.data.externalUserId};this.hasReceivedResult=!0,p("VERIFICATION_COMPLETED",{status:u.status,sessionId:n.substring(0,8)+"...",origin:o.origin},this.brandConstants.name),this.cleanup({closePopup:!1}),this.unlockVerification(),this.popupMonitorInterval&&(clearInterval(this.popupMonitorInterval),this.popupMonitorInterval=null),u.status==="verified"?($=(O=this.config).onComplete)==null||$.call(O,u):(K=(N=this.config).onError)==null||K.call(N,new Error(`Verification failed: ${u.status}`))},window.addEventListener("message",this.messageListener),this.popupMonitorInterval=setInterval(()=>{this.popupWindow&&this.popupWindow.closed&&(p("POPUP_CLOSED_BY_USER",{sessionId:n.substring(0,8)+"...",environment:this.config.environment},this.brandConstants.name),this.hasReceivedResult||this.handleCancellation(n,"popup-closed"))},500)}setupAutoCleanup(){if(this.unloadListener=()=>{p("SDK_AUTO_CLEANUP",{environment:this.config.environment,trigger:"page_unload"},this.brandConstants.name),this.cleanup(),this.unlockVerification()},window.addEventListener("beforeunload",this.unloadListener),window.addEventListener("pagehide",this.unloadListener),window.history&&window.history.pushState){let e=window.history.pushState;window.history.pushState=(...n)=>(this.cleanup(),this.unlockVerification(),e.apply(window.history,n))}}getEnvironment(){return this.config.environment}unlockVerification(){this.isVerificationInProgress=!1,this.currentSessionId=null,p("VERIFICATION_UNLOCKED",{environment:this.config.environment,origin:window.location.origin},this.brandConstants.name)}cleanup(e={}){let n=e.closePopup!==!1;this.popupWindow&&(n&&!this.popupWindow.closed&&this.popupWindow.close(),(n||this.popupWindow.closed)&&(this.popupWindow=null)),this.messageListener&&(window.removeEventListener("message",this.messageListener),this.messageListener=null),this.popupMonitorInterval&&(clearInterval(this.popupMonitorInterval),this.popupMonitorInterval=null)}handleCancellation(e,n){if(this.hasReceivedResult)return;this.hasReceivedResult=!0,p("VERIFICATION_CANCELLED",{source:n,sessionId:e.substring(0,8)+"...",environment:this.config.environment,origin:window.location.origin},this.brandConstants.name),this.cleanup(),this.unlockVerification();let r=!0;if(this.config.onCancel)try{this.config.onCancel()===!1&&(r=!1)}catch(i){p("CANCEL_CALLBACK_FAILED",{error:i instanceof Error?i.message:String(i),sessionId:e.substring(0,8)+"..."},this.brandConstants.name)}r&&this.redirectToCancelUrl(e)}redirectToCancelUrl(e){if(this.config.cancelUrl)try{let n=decodeURIComponent(this.config.cancelUrl),r=new URL(n);r.searchParams.set("sessionId",e),r.searchParams.set("status","cancelled"),r.searchParams.set("timestamp",Date.now().toString()),this.lastExternalUserId&&r.searchParams.set("externalUserId",this.lastExternalUserId),window.location.href=r.toString()}catch(n){p("CANCEL_REDIRECT_FAILED",{error:n instanceof Error?n.message:String(n),sessionId:e.substring(0,8)+"..."},this.brandConstants.name)}}removeAutoCleanupListeners(){this.unloadListener&&(window.removeEventListener("beforeunload",this.unloadListener),window.removeEventListener("pagehide",this.unloadListener),this.unloadListener=null)}destroy(){p("SDK_DESTROYED",{environment:this.config.environment,origin:window.location.origin},this.brandConstants.name),this.cleanup(),this.unlockVerification(),this.removeAutoCleanupListeners()}getPortalApiUrl(){return this.config.apiUrl?this.config.apiUrl:this.getUrlConfig().apiUrl}getEngineUrl(){return this.getUrlConfig().engineUrl}getWebSocketUrl(){return this.getUrlConfig().wsUrl}isPublicKey(){return this.config.apiKey.startsWith("pk_")}async createInternalSession(e){var n,r,i;try{let s=this.getPortalApiUrl(),l=await fetch(`${s}/api/v1/sessions/create`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.config.apiKey}`},body:JSON.stringify({merchantId:this.config.apiKey,returnUrl:this.config.returnUrl,cancelUrl:this.config.cancelUrl,challengeAge:e.challengeAge,verificationMode:e.verificationMode,faceMatchEnabled:(n=e.faceMatchEnabled)!=null?n:this.config.faceMatchEnabled,merchantName:document.title||window.location.hostname,externalUserId:e.externalUserId})});if(!l.ok){let d=await l.json().catch(()=>({})),o=d==null?void 0:d.code;if(this.isBillingBlockError(o)){let g=e.language||this.config.language;this.openBillingBlockPage(o,d==null?void 0:d.portalUrl,g)}throw new Error(`Failed to create session: ${l.status} ${l.statusText}. ${d.message||""}`)}let a=await l.json(),c=a.sessionId;if(!c)throw new Error("Server did not return a sessionId");return a.verifyUrl&&(this.lastVerifyUrl=a.verifyUrl),a.sessionToken&&(this.lastSessionToken=a.sessionToken),a.handoffToken&&(this.temporaryHandoffToken=a.handoffToken),typeof a.sandboxMode=="boolean"?this.lastSandboxMode=a.sandboxMode:this.lastSandboxMode=null,p("INTERNAL_SESSION_CREATED",{sessionId:c.substring(0,8)+"...",environment:this.config.environment,apiKeyType:"public"},this.brandConstants.name),c}catch(s){let l=s instanceof Error?s.message:String(s);throw p("INTERNAL_SESSION_FAILED",{error:l,environment:this.config.environment,apiKeyType:"public"},this.brandConstants.name),(i=(r=this.config).onError)==null||i.call(r,s),new Error(`Failed to create verification session: ${l}`)}}isBillingBlockError(e){return e==="SUBSCRIPTION_REQUIRED"||e==="PLAN_LIMIT_REACHED"||e==="SANDBOX_LIMIT_REACHED"}openBillingBlockPage(e,n,r){var i,s,l,a;try{let c=this.config.verifyUrl||E(this.config.environment,this.getUrlConfig()),d=this.applyLocalVerifyOverride(c),o=new URL(d);o.searchParams.set("blocked",e),n&&o.searchParams.set("portalUrl",n);let g=r||this.config.language;if(g&&o.searchParams.set("lang",g),this.config.mode==="new-tab"){((this.config.newTabTarget||"popup")==="tab"?window.open(o.toString(),"_blank"):window.open(o.toString(),this.brandConstants.popupName,"width=600,height=700"))||(s=(i=this.config).onError)==null||s.call(i,new Error("Failed to open billing notice window. Please check popup blocker settings."));return}this.redirect(o.toString())}catch(c){let d=c instanceof Error?c.message:String(c);(a=(l=this.config).onError)==null||a.call(l,new Error(`Failed to open billing notice: ${d}`))}}getUrlConfig(){return this.brandUrls[this.config.environment]||this.brandUrls.production}getTrustedOrigins(){return this.getUrlConfig().trustedOrigins}getAllowedCustomOrigins(e){let n=new Set,r=this.getLocalOrigin(this.config.verifyUrl||null),i=this.getLocalOrigin(e||null);return r&&n.add(r),i&&n.add(i),Array.from(n)}getLocalOrigin(e){if(!e)return null;try{let n=new URL(e);if(I.LOCAL_HOSTNAMES.has(n.hostname))return n.origin}catch(n){return null}return null}applyLocalVerifyOverride(e){let n=this.getLocalOrigin(this.config.verifyUrl||null);if(!n)return e;try{let r=new URL(n),i=new URL(e);return i.protocol=r.protocol,i.host=r.host,i.toString()}catch(r){return e}}getHmacSecret(){return this.config.environment==="staging"?this.brandConstants.hmacSecretStaging:this.brandConstants.hmacSecretProd}};I.LOCAL_HOSTNAMES=new Set(["localhost","127.0.0.1","::1"]);var C=I;var de={production:{apiUrl:"https://api.privateav.com",verifyUiUrl:"https://verify.privateav.com",engineUrl:"https://engine.privateav.com",wsUrl:"wss://engine.privateav.com/api/websocket/stream",trustedOrigins:["https://verify.privateav.com","https://portal.privateav.com","https://api.privateav.com"]},staging:{apiUrl:"https://api.verityav-staging-usw1a.privateav.com",verifyUiUrl:"https://verify.verityav-staging-usw1a.privateav.com",engineUrl:"https://engine.verityav-staging-usw1a.privateav.com",wsUrl:"wss://engine.verityav-staging-usw1a.privateav.com/api/websocket/stream",trustedOrigins:["https://verify.verityav-staging-usw1a.privateav.com","https://portal.verityav-staging-usw1a.privateav.com","https://api.verityav-staging-usw1a.privateav.com"]}},L={name:"PrivateAV",hmacSecretProd:"privateav-prod-hmac-2025",hmacSecretStaging:"privateav-stage-hmac-2025",messageType:"privateav:verification:complete",legacyMessageType:"privateav-verification",popupName:"privateav-verify",docsUrl:"https://docs.privateav.com"};var w=class extends C{constructor(e){super(e,de,L)}},ge="3.5.3";w.VERSION=ge;typeof window!="undefined"&&(j(),q(`${L.name} SDK`));var Pe=w;return ve(xe);})();
1
+ /* PrivateAV SDK v3.5.6 */
2
+ "use strict";var PrivateAVSDK=(()=>{var v=Object.defineProperty,Q=Object.defineProperties,ee=Object.getOwnPropertyDescriptor,te=Object.getOwnPropertyDescriptors,re=Object.getOwnPropertyNames,D=Object.getOwnPropertySymbols;var $=Object.prototype.hasOwnProperty,ie=Object.prototype.propertyIsEnumerable;var _=(r,e,t)=>e in r?v(r,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):r[e]=t,u=(r,e)=>{for(var t in e||(e={}))$.call(e,t)&&_(r,t,e[t]);if(D)for(var t of D(e))ie.call(e,t)&&_(r,t,e[t]);return r},N=(r,e)=>Q(r,te(e));var ne=(r,e)=>{for(var t in e)v(r,t,{get:e[t],enumerable:!0})},se=(r,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of re(e))!$.call(r,n)&&n!==t&&v(r,n,{get:()=>e[n],enumerable:!(i=ee(e,n))||i.enumerable});return r};var oe=r=>se(v({},"__esModule",{value:!0}),r);var he={};ne(he,{PrivateAV:()=>f,VERSION:()=>J,default:()=>fe});function k(){crypto.randomUUID||(crypto.randomUUID=function(){let r=new Uint8Array(16);crypto.getRandomValues(r),r[6]=r[6]&15|64,r[8]=r[8]&63|128;let e=Array.from(r).map(t=>t.toString(16).padStart(2,"0")).join("");return[e.slice(0,8),e.slice(8,12),e.slice(12,16),e.slice(16,20),e.slice(20,32)].join("-")})}function M(r="SDK"){let e=[];if(!window.crypto||!window.crypto.getRandomValues)throw new Error(`${r} requires Web Crypto API support`);crypto.randomUUID||e.push("crypto.randomUUID not supported, using polyfill"),window.URLSearchParams||e.push("URLSearchParams not supported, consider adding a polyfill for IE 11 support"),e.length>0&&console.warn(`${r} Browser Compatibility:`,e.join("; "))}function ae(r,e){return e.includes(r)}function K(r,e,t=[],i="SDK"){var s;let{origin:n}=r;return ae(n,e)||t.length>0&&t.some(l=>{if(l.startsWith("*.")){let c=l.slice(2);return n.endsWith(`.${c}`)||n===`https://${c}`||n===`http://${c}`}return n===l})?!0:(console.warn(`${i} Security: Blocked PostMessage from untrusted origin: ${n}`,{trustedOrigins:e,allowedCustomOrigins:t,eventType:(s=r.data)==null?void 0:s.type}),!1)}function B(r,e,t,i){let{data:n}=r;return!n||typeof n!="object"?{isValid:!1,error:"Invalid message format"}:(i?[t,i]:[t]).includes(n.type)?!n.sessionId||n.sessionId!==e?{isValid:!1,error:"Session ID mismatch"}:!n.status||!["verified","failed","cancelled"].includes(n.status)?{isValid:!1,error:"Invalid status value"}:{isValid:!0}:{isValid:!1,error:"Invalid message type"}}function W(r,e="SDK"){r==="production"&&window.location.protocol!=="https:"&&console.warn(`${e} Warning: HTTPS recommended for production environment`,{current:window.location.href})}function S(r,e,t="SDK"){try{let i=new URL(r);if(i.protocol==="file:")return{isValid:!1,error:"file:// URLs are not supported. The verification redirect cannot return to local files. Please use a local web server (e.g., npx serve . or python3 -m http.server) instead of opening the HTML file directly."};if(i.protocol!=="https:"&&!(i.hostname==="localhost"||i.hostname==="127.0.0.1"))return{isValid:!1,error:`HTTPS required for return URLs in ${e}`};let n=[/data:/i,/javascript:/i,/vbscript:/i,/file:/i,/ftp:/i];for(let s of n)if(s.test(r))return{isValid:!1,error:"Blocked suspicious URL scheme"};return{isValid:!0}}catch(i){return{isValid:!1,error:"Invalid URL format"}}}var E=class{constructor(){this.attempts=new Map;this.maxAttempts=5;this.timeWindow=6e4}isAllowed(e,t="SDK"){let i=Date.now(),s=(this.attempts.get(e)||[]).filter(o=>i-o<this.timeWindow);return s.length>=this.maxAttempts?(console.warn(`${t} Security: Rate limit exceeded for ${e}`),!1):(s.push(i),this.attempts.set(e,s),!0)}reset(e){this.attempts.delete(e)}},F=new E;function d(r,e,t="SDK"){let i={timestamp:new Date().toISOString(),userAgent:typeof navigator!="undefined"?navigator.userAgent:"unknown",url:typeof window!="undefined"?window.location.href:"unknown"};console.warn(`${t} Security Event: ${r}`,u(u({},i),e))}var H=25,q=150,w=2048,j=128,le=/^(pk_|sk_)[a-zA-Z0-9_]+$/;function G(r,e){var n;if(!r.apiKey)throw new Error("apiKey is required");if(r.apiKey.length>j)throw new Error(`apiKey exceeds maximum length of ${j} characters`);if(!le.test(r.apiKey))throw new Error("Invalid apiKey format. Expected pk_xxx (public) or sk_xxx (private)");if(typeof window!="undefined"&&r.apiKey.startsWith("sk_"))throw new Error(`Secret keys (sk_) should never be used in browser code for security reasons. Secret keys expose your account to unauthorized access if used client-side. Please use your public key (pk_) instead. If you need to use features that require a secret key (like custom challenge age), create the session server-side and pass the sessionId to startVerificationWithSession(). See: ${e.docsUrl}/server-side-sessions`);if(!r.returnUrl)throw new Error("returnUrl is required");if(r.returnUrl.length>w)throw new Error(`returnUrl exceeds maximum length of ${w} characters`);let t=(n=e.environment)!=null?n:"production",i=S(r.returnUrl,t,e.brandName);if(!i.isValid)throw new Error(`returnUrl validation failed: ${i.error}`);if(r.cancelUrl){if(r.cancelUrl.length>w)throw new Error(`cancelUrl exceeds maximum length of ${w} characters`);let s=S(r.cancelUrl,t,e.brandName);if(!s.isValid)throw new Error(`cancelUrl validation failed: ${s.error}`)}if(r.defaultChallengeAge!==void 0){if(r.defaultChallengeAge<H)throw new Error(`defaultChallengeAge must be at least ${H}`);if(r.defaultChallengeAge>q)throw new Error(`defaultChallengeAge cannot exceed ${q}`)}if(r.defaultVerificationMode&&!["L1","L2"].includes(r.defaultVerificationMode))throw new Error("defaultVerificationMode must be L1 or L2");if(r.faceMatchEnabled!==void 0&&typeof r.faceMatchEnabled!="boolean")throw new Error("faceMatchEnabled must be a boolean");if(r.mode&&!["redirect","new-tab"].includes(r.mode))throw new Error("mode must be redirect or new-tab");if(r.newTabTarget&&!["popup","tab"].includes(r.newTabTarget))throw new Error("newTabTarget must be popup or tab")}function b(r,e){let t=e.verifyUiUrl;if(!t||!t.startsWith("https://"))throw new Error(`HTTPS required for ${r} environment`);return t}function ce(r,e){let t=e.apiUrl;if(!t||!t.startsWith("https://"))throw new Error(`HTTPS required for API URLs in ${r} environment`);return t}function de(r){if(!r)return null;try{return new URL(r).hostname.toLowerCase()}catch(e){return null}}function ge(r){let e=r.split(".");return e.length<=2?r:e.slice(1).join(".")}function Z(r){let e=new Set;for(let t of r){let i=de(t);i&&e.add(ge(i))}return e}function pe(r){var s,o;if(!r||!r.staging)return[];let e=r.staging,t=Z([e.apiUrl,e.verifyUiUrl,e.engineUrl,e.wsUrl,...(s=e.trustedOrigins)!=null?s:[]]),i=r.production,n=i?Z([i.apiUrl,i.verifyUiUrl,i.engineUrl,i.wsUrl,...(o=i.trustedOrigins)!=null?o:[]]):new Set;for(let l of n)t.delete(l);return Array.from(t)}function ue(r,e){let t=(r||"").toLowerCase();return t?pe(e).some(i=>t===i||t.endsWith(`.${i}`)):!1}function X(r,e){return r==="staging"||r==="production"?r:r||typeof window=="undefined"||!window.location?"production":ue(window.location.hostname,e)?"staging":"production"}function z(r,e,t="SDK"){let i=window.location.protocol==="https:";switch(r){case"production":i||console.warn(`${t} Warning: HTTPS recommended for production environment`);break;case"staging":i||console.warn(`${t} Warning: HTTPS strongly recommended in staging environment`);break}try{b(r,e),ce(r,e)}catch(n){let s=n instanceof Error?n.message:String(n);throw new Error(`Environment configuration validation failed: ${s}`)}}var y=class y{constructor(e,t,i){this.popupWindow=null;this.messageListener=null;this.popupMonitorInterval=null;this.unloadListener=null;this.isVerificationInProgress=!1;this.currentSessionId=null;this.hasReceivedResult=!1;this.lastVerifyUrl=null;this.lastExternalUserId=null;this.brandUrls=t,this.brandConstants=i;let n=X(e.environment,this.brandUrls);e.environment&&e.environment!=="staging"&&e.environment!=="production"&&console.warn(`${this.brandConstants.name} SDK: Unknown environment '${e.environment}', defaulting to 'production'`),G(e,{brandName:this.brandConstants.name,docsUrl:this.brandConstants.docsUrl,environment:n}),this.config=N(u({},e),{environment:n,mode:e.mode||"redirect",newTabTarget:e.newTabTarget||"popup"}),this.validateServiceOverrides(),z(this.config.environment,this.getUrlConfig(),this.brandConstants.name),W(this.config.environment,this.brandConstants.name),d("SDK_INITIALIZED",{environment:this.config.environment,mode:this.config.mode,origin:window.location.origin,protocol:window.location.protocol,hostname:window.location.hostname},this.brandConstants.name),this.setupAutoCleanup()}async verify(e={}){var n,s,o,l,c,g;let t=this.isPublicKey(),i;if(t)i=await this.createInternalSession(e);else throw new Error("Private API keys (sk_) should use the direct API, not the SDK. The SDK is designed for browser-based public key usage only.");if(!i)throw new Error("Failed to obtain sessionId from server");if(this.isVerificationInProgress){let a=new Error(`Verification already in progress for session ${(n=this.currentSessionId)==null?void 0:n.substring(0,8)}...`);throw d("RACE_CONDITION_PREVENTED",{currentSession:((s=this.currentSessionId)==null?void 0:s.substring(0,8))+"...",attemptedSession:"new-session-attempt",origin:window.location.origin},this.brandConstants.name),(l=(o=this.config).onError)==null||l.call(o,a),a}this.isVerificationInProgress=!0,this.currentSessionId=i,this.lastExternalUserId=e.externalUserId||null;try{let a=`${this.config.apiKey}:${window.location.origin}`;if(!F.isAllowed(a,this.brandConstants.name)){let h=new Error("Too many verification attempts. Please wait before trying again.");throw d("RATE_LIMIT_EXCEEDED",{apiKey:this.config.apiKey.substring(0,8)+"...",origin:window.location.origin,sessionId:i?i.substring(0,8)+"...":"undefined"},this.brandConstants.name),(g=(c=this.config).onError)==null||g.call(c,h),h}let p=await this.buildVerificationUrl(e,i);d("VERIFICATION_INITIATED",{environment:this.config.environment,mode:this.config.mode,sessionId:i?i.substring(0,8)+"...":"undefined",origin:window.location.origin},this.brandConstants.name),this.config.mode==="new-tab"?this.openNewTab(p,i):(this.unlockVerification(),this.redirect(p))}catch(a){throw this.unlockVerification(),a}}async buildVerificationUrl(e,t){if(!this.lastVerifyUrl)throw new Error("Server did not return an authoritative verification URL");let i=e.language||this.config.language,n=new URL(this.lastVerifyUrl),s=new URL(this.getUrlConfig().verifyUiUrl).origin;if(n.origin!==s)throw new Error("Server returned an untrusted verification origin");if(n.searchParams.get("sessionId")!==t||!n.searchParams.get("sessionToken"))throw new Error("Server verification URL is missing authoritative session context");let o=new URL(this.applyLocalVerifyOverride(n.toString()));return o.searchParams.set("mode",this.config.mode),e.skipIntro&&o.searchParams.set("skip_intro","true"),e.autoReturn&&o.searchParams.set("auto_return","true"),i&&o.searchParams.set("lang",i),o.toString()}redirect(e){window.location.href=e}openNewTab(e,t){var c,g;if(this.cleanup(),this.hasReceivedResult=!1,this.popupMonitorInterval&&(clearInterval(this.popupMonitorInterval),this.popupMonitorInterval=null),(this.config.newTabTarget||"popup")==="tab"?this.popupWindow=window.open(e,"_blank"):this.popupWindow=window.open(e,this.brandConstants.popupName,"width=600,height=700"),!this.popupWindow){(g=(c=this.config).onError)==null||g.call(c,new Error("Failed to open verification window. Please check popup blocker settings."));return}let n=this.getTrustedOrigins(),s=this.getAllowedCustomOrigins(e),o=this.brandConstants.messageType,l=this.brandConstants.legacyMessageType;this.messageListener=a=>{var L,A,R,O,P,V,x;let p=(L=a.data)==null?void 0:L.type;if(!p||typeof p!="string"||!(l?[o,l]:[o]).includes(p))return;if(!K(a,n,s,this.brandConstants.name)){d("POSTMESSAGE_ORIGIN_BLOCKED",{origin:a.origin,environment:this.config.environment,expectedOrigins:`${this.brandConstants.name} trusted origins for ${this.config.environment}`,messageType:(A=a.data)==null?void 0:A.type},this.brandConstants.name);return}let I=B(a,t,o,l);if(!I.isValid){d("POSTMESSAGE_VALIDATION_FAILED",{error:I.error,origin:a.origin,sessionId:t.substring(0,8)+"...",messageType:(R=a.data)==null?void 0:R.type},this.brandConstants.name);return}let T=a.data.status;if(T==="cancelled"){this.handleCancellation(t,"postmessage");return}let m={sessionId:a.data.sessionId,status:T,timestamp:a.data.timestamp,externalUserId:a.data.externalUserId};this.hasReceivedResult=!0,d("VERIFICATION_COMPLETED",{status:m.status,sessionId:t.substring(0,8)+"...",origin:a.origin},this.brandConstants.name),this.cleanup({closePopup:!1}),this.unlockVerification(),this.popupMonitorInterval&&(clearInterval(this.popupMonitorInterval),this.popupMonitorInterval=null),m.status==="verified"?(P=(O=this.config).onComplete)==null||P.call(O,m):(x=(V=this.config).onError)==null||x.call(V,new Error(`Verification failed: ${m.status}`))},window.addEventListener("message",this.messageListener),this.popupMonitorInterval=setInterval(()=>{this.popupWindow&&this.popupWindow.closed&&(d("POPUP_CLOSED_BY_USER",{sessionId:t.substring(0,8)+"...",environment:this.config.environment},this.brandConstants.name),this.hasReceivedResult||this.handleCancellation(t,"popup-closed"))},500)}setupAutoCleanup(){if(this.unloadListener=()=>{d("SDK_AUTO_CLEANUP",{environment:this.config.environment,trigger:"page_unload"},this.brandConstants.name),this.cleanup(),this.unlockVerification()},window.addEventListener("beforeunload",this.unloadListener),window.addEventListener("pagehide",this.unloadListener),window.history&&window.history.pushState){let e=window.history.pushState;window.history.pushState=(...t)=>(this.cleanup(),this.unlockVerification(),e.apply(window.history,t))}}getEnvironment(){return this.config.environment}unlockVerification(){this.isVerificationInProgress=!1,this.currentSessionId=null,d("VERIFICATION_UNLOCKED",{environment:this.config.environment,origin:window.location.origin},this.brandConstants.name)}cleanup(e={}){let t=e.closePopup!==!1;this.popupWindow&&(t&&!this.popupWindow.closed&&this.popupWindow.close(),(t||this.popupWindow.closed)&&(this.popupWindow=null)),this.messageListener&&(window.removeEventListener("message",this.messageListener),this.messageListener=null),this.popupMonitorInterval&&(clearInterval(this.popupMonitorInterval),this.popupMonitorInterval=null)}handleCancellation(e,t){if(this.hasReceivedResult)return;this.hasReceivedResult=!0,d("VERIFICATION_CANCELLED",{source:t,sessionId:e.substring(0,8)+"...",environment:this.config.environment,origin:window.location.origin},this.brandConstants.name),this.cleanup(),this.unlockVerification();let i=!0;if(this.config.onCancel)try{this.config.onCancel()===!1&&(i=!1)}catch(n){d("CANCEL_CALLBACK_FAILED",{error:n instanceof Error?n.message:String(n),sessionId:e.substring(0,8)+"..."},this.brandConstants.name)}i&&this.redirectToCancelUrl(e)}redirectToCancelUrl(e){if(this.config.cancelUrl)try{let t=decodeURIComponent(this.config.cancelUrl),i=new URL(t);i.searchParams.set("sessionId",e),i.searchParams.set("status","cancelled"),i.searchParams.set("timestamp",Date.now().toString()),this.lastExternalUserId&&i.searchParams.set("externalUserId",this.lastExternalUserId),window.location.href=i.toString()}catch(t){d("CANCEL_REDIRECT_FAILED",{error:t instanceof Error?t.message:String(t),sessionId:e.substring(0,8)+"..."},this.brandConstants.name)}}removeAutoCleanupListeners(){this.unloadListener&&(window.removeEventListener("beforeunload",this.unloadListener),window.removeEventListener("pagehide",this.unloadListener),this.unloadListener=null)}destroy(){d("SDK_DESTROYED",{environment:this.config.environment,origin:window.location.origin},this.brandConstants.name),this.cleanup(),this.unlockVerification(),this.removeAutoCleanupListeners()}getPortalApiUrl(){let e=this.getUrlConfig().apiUrl;if(!this.config.apiUrl)return e;let t=this.getUrlOrigin(this.config.apiUrl),i=this.getUrlOrigin(e);if(t===i||this.getLocalOrigin(this.config.apiUrl))return this.config.apiUrl;throw new Error("apiUrl must use the selected brand/environment service")}validateServiceOverrides(){let e=this.getUrlConfig();if(this.config.apiUrl){let t=this.getUrlOrigin(this.config.apiUrl),i=this.getUrlOrigin(e.apiUrl);if(t!==i&&!this.getLocalOrigin(this.config.apiUrl))throw new Error("apiUrl must use the selected brand/environment service")}if(this.config.verifyUrl){let t=this.getUrlOrigin(this.config.verifyUrl),i=this.getUrlOrigin(e.verifyUiUrl);if(t!==i&&!this.getLocalOrigin(this.config.verifyUrl))throw new Error("verifyUrl must use the selected brand/environment service")}}isPublicKey(){return this.config.apiKey.startsWith("pk_")}async createInternalSession(e){var t,i;try{let n=this.getPortalApiUrl(),s=await fetch(`${n}/api/v1/sessions/create`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.config.apiKey}`},body:JSON.stringify({merchantId:this.config.apiKey,returnUrl:this.config.returnUrl,cancelUrl:this.config.cancelUrl,merchantName:document.title||window.location.hostname,externalUserId:e.externalUserId})});if(!s.ok){let c=await s.json().catch(()=>({})),g=c==null?void 0:c.code;if(this.isBillingBlockError(g)){let a=e.language||this.config.language;this.openBillingBlockPage(g,a)}throw new Error(`Failed to create session: ${s.status} ${s.statusText}. ${c.message||""}`)}let o=await s.json(),l=o.sessionId;if(!l)throw new Error("Server did not return a sessionId");return o.verifyUrl&&(this.lastVerifyUrl=o.verifyUrl),d("INTERNAL_SESSION_CREATED",{sessionId:l.substring(0,8)+"...",environment:this.config.environment,apiKeyType:"public"},this.brandConstants.name),l}catch(n){let s=n instanceof Error?n.message:String(n);throw d("INTERNAL_SESSION_FAILED",{error:s,environment:this.config.environment,apiKeyType:"public"},this.brandConstants.name),(i=(t=this.config).onError)==null||i.call(t,n),new Error(`Failed to create verification session: ${s}`)}}isBillingBlockError(e){return e==="SUBSCRIPTION_REQUIRED"||e==="PLAN_LIMIT_REACHED"||e==="SANDBOX_LIMIT_REACHED"}openBillingBlockPage(e,t){var i,n,s,o;try{let l=b(this.config.environment,this.getUrlConfig()),c=this.applyLocalVerifyOverride(l),g=new URL(c);g.searchParams.set("blocked",e);let a=t||this.config.language;if(a&&g.searchParams.set("lang",a),this.config.mode==="new-tab"){((this.config.newTabTarget||"popup")==="tab"?window.open(g.toString(),"_blank"):window.open(g.toString(),this.brandConstants.popupName,"width=600,height=700"))||(n=(i=this.config).onError)==null||n.call(i,new Error("Failed to open billing notice window. Please check popup blocker settings."));return}this.redirect(g.toString())}catch(l){let c=l instanceof Error?l.message:String(l);(o=(s=this.config).onError)==null||o.call(s,new Error(`Failed to open billing notice: ${c}`))}}getUrlConfig(){return this.brandUrls[this.config.environment]||this.brandUrls.production}getTrustedOrigins(){return this.getUrlConfig().trustedOrigins}getAllowedCustomOrigins(e){let t=new Set,i=this.getLocalOrigin(this.config.verifyUrl||null),n=this.getLocalOrigin(e||null);return i&&t.add(i),n&&t.add(n),Array.from(t)}getLocalOrigin(e){if(!e)return null;try{let t=new URL(e);if(y.LOCAL_HOSTNAMES.has(t.hostname))return t.origin}catch(t){return null}return null}getUrlOrigin(e){try{return new URL(e).origin}catch(t){return null}}applyLocalVerifyOverride(e){let t=this.getLocalOrigin(this.config.verifyUrl||null);if(!t)return e;try{let i=new URL(t),n=new URL(e);return n.protocol=i.protocol,n.host=i.host,n.toString()}catch(i){return e}}};y.LOCAL_HOSTNAMES=new Set(["localhost","127.0.0.1","::1"]);var U=y;var Y={production:{apiUrl:"https://api.privateav.com",verifyUiUrl:"https://verify.privateav.com",engineUrl:"https://engine.privateav.com",wsUrl:"wss://engine.privateav.com/api/websocket/stream",trustedOrigins:["https://verify.privateav.com"]},staging:{apiUrl:"https://api.staging.privateav.com",verifyUiUrl:"https://verify.staging.privateav.com",engineUrl:"https://engine.staging.privateav.com",wsUrl:"wss://engine.staging.privateav.com/api/websocket/stream",trustedOrigins:["https://verify.staging.privateav.com"]}},C={name:"PrivateAV",messageType:"privateav:verification:complete",legacyMessageType:"privateav-verification",popupName:"privateav-verify",docsUrl:"https://docs.privateav.com"};var f=class extends U{constructor(e){super(e,Y,C)}},J="3.5.6";f.VERSION=J;typeof window!="undefined"&&(k(),M(`${C.name} SDK`));var fe=f;return oe(he);})();
3
3
  if(typeof PrivateAVSDK !== "undefined" && PrivateAVSDK.PrivateAV) { window.PrivateAV = PrivateAVSDK.PrivateAV; window.PrivateAV.VERSION = PrivateAVSDK.VERSION; }
package/types/base.d.ts CHANGED
@@ -32,18 +32,18 @@ export interface SDKConfig {
32
32
  */
33
33
  newTabTarget?: 'popup' | 'tab';
34
34
  /**
35
- * Default challenge age (minimum 25)
36
- * Can be overridden per verification
35
+ * @deprecated Browser SDK policy overrides are ignored. Configure verification
36
+ * policy in the dashboard or create the session server-side with a private key.
37
37
  */
38
38
  defaultChallengeAge?: number;
39
39
  /**
40
- * Default verification mode
41
- * Can be overridden per verification
40
+ * @deprecated Browser SDK policy overrides are ignored. Configure verification
41
+ * policy in the dashboard or create the session server-side with a private key.
42
42
  */
43
43
  defaultVerificationMode?: 'L1' | 'L2';
44
44
  /**
45
- * Default face match setting for verification sessions
46
- * @default true
45
+ * @deprecated Browser SDK policy overrides are ignored. Configure verification
46
+ * policy server-side.
47
47
  */
48
48
  faceMatchEnabled?: boolean;
49
49
  /**
@@ -76,20 +76,18 @@ export interface SDKConfig {
76
76
  }
77
77
  export interface VerificationOptions {
78
78
  /**
79
- * Minimum age to verify (minimum 25)
80
- * @default Uses merchant dashboard configuration
79
+ * @deprecated Browser SDK policy overrides are ignored. Configure verification
80
+ * policy in the dashboard or create the session server-side with a private key.
81
81
  */
82
82
  challengeAge?: number;
83
83
  /**
84
- * Verification mode
85
- * L1: Age estimation allowed if user appears older
86
- * L2: Full ID verification required
87
- * @default Uses merchant dashboard configuration
84
+ * @deprecated Browser SDK policy overrides are ignored. Configure verification
85
+ * policy in the dashboard or create the session server-side with a private key.
88
86
  */
89
87
  verificationMode?: 'L1' | 'L2';
90
88
  /**
91
- * Whether biometric selfie-vs-ID face matching is enabled for this verification
92
- * @default true
89
+ * @deprecated Browser SDK policy overrides are ignored. Configure verification
90
+ * policy server-side.
93
91
  */
94
92
  faceMatchEnabled?: boolean;
95
93
  /**
@@ -126,7 +124,7 @@ export interface VerificationResult {
126
124
  * Binary result: 'verified' or 'failed'
127
125
  * Full details available via server-side API
128
126
  */
129
- status: 'verified' | 'failed';
127
+ status: 'verified' | 'failed' | 'cancelled';
130
128
  /**
131
129
  * Timestamp when verification completed (milliseconds since epoch)
132
130
  */
@@ -136,32 +134,6 @@ export interface VerificationResult {
136
134
  */
137
135
  externalUserId?: string;
138
136
  }
139
- export interface StatePayload {
140
- merchantId: string;
141
- sessionId: string;
142
- returnUrl: string;
143
- /** Optional cancel redirect for new-tab abandonment */
144
- cancelUrl?: string;
145
- challengeAge?: number;
146
- verificationMode?: 'L1' | 'L2';
147
- faceMatchEnabled?: boolean;
148
- hasOverrides?: boolean;
149
- externalUserId?: string;
150
- timestamp: number;
151
- apiUrl?: string;
152
- engineUrl?: string;
153
- wsUrl?: string;
154
- environment?: 'production' | 'staging';
155
- features?: {
156
- testMode: boolean;
157
- warmupPeriodMs: number;
158
- qualityThreshold: number;
159
- sandboxMode?: boolean;
160
- };
161
- handoffToken?: string;
162
- sessionToken?: string;
163
- verifyUrl?: string;
164
- }
165
137
  export interface SessionValidationResponse {
166
138
  sessionId: string;
167
139
  merchantId: string;
@@ -9,9 +9,7 @@ export declare function getApiUrl(environment: 'production' | 'staging', urls: U
9
9
  * config (never hardcoded). Any production zone is excluded so that a
10
10
  * production host can never be misclassified as first-party staging.
11
11
  *
12
- * For the current brand configs this yields a single zone, e.g.
13
- * SafePassage -> 'verityav-staging-usw1a.safepassageapp.com'
14
- * PrivateAV -> 'verityav-staging-usw1a.privateav.com'
12
+ * For each current brand config this yields its internal staging parent zone.
15
13
  */
16
14
  export declare function getFirstPartyStagingHosts(brandUrls: BrandUrls): string[];
17
15
  /**
@@ -1,12 +1,11 @@
1
1
  /**
2
2
  * Validation utilities for the SDK
3
3
  */
4
- import type { SDKConfig, StatePayload } from '../types/base';
4
+ import type { SDKConfig } from '../types/base';
5
5
  export declare const MINIMUM_AGE = 25;
6
6
  export declare const MAXIMUM_AGE = 150;
7
7
  export declare const MAX_URL_LENGTH = 2048;
8
8
  export declare const MAX_API_KEY_LENGTH = 128;
9
- export declare const STATE_EXPIRY_MS = 600000;
10
9
  export interface ValidationContext {
11
10
  brandName: string;
12
11
  docsUrl: string;
@@ -22,5 +21,3 @@ export interface ValidationContext {
22
21
  export declare function validateConfig(config: SDKConfig, context: ValidationContext): void;
23
22
  export declare function validateSessionId(sessionId: string): void;
24
23
  export declare function validateChallengeAge(age?: number): void;
25
- export declare function generateState(payload: StatePayload, environment: 'production' | 'staging', hmacSecret: string, _logLabel?: string): Promise<string>;
26
- export declare function parseState(state: string, environment: 'production' | 'staging', hmacSecret: string, logLabel?: string): Promise<StatePayload | null>;
package/utils/crypto.d.ts DELETED
@@ -1,31 +0,0 @@
1
- /**
2
- * Crypto utilities for browser-compatible SDKs.
3
- *
4
- * These utilities handle HMAC signing, state parameter protection, and secure
5
- * token generation for client-side tamper resistance.
6
- *
7
- * IMPORTANT SECURITY NOTE:
8
- * Client-side cryptography provides defense-in-depth but cannot be considered
9
- * secure against determined attackers. Server-side validation is required for
10
- * real security.
11
- */
12
- /**
13
- * Generate HMAC-SHA256 signature using Web Crypto API
14
- */
15
- export declare function generateHMAC(data: string, secret: string): Promise<string>;
16
- /**
17
- * Verify HMAC-SHA256 signature using Web Crypto API
18
- */
19
- export declare function verifyHMAC(data: string, signature: string, secret: string): Promise<boolean>;
20
- /**
21
- * Generate a secure random token for client-side use
22
- */
23
- export declare function generateSecureToken(length?: number): string;
24
- /**
25
- * Create signed state parameter with timestamp and integrity protection
26
- */
27
- export declare function createSignedState(payload: unknown, hmacSecret: string): Promise<string>;
28
- /**
29
- * Verify and parse signed state parameter
30
- */
31
- export declare function parseSignedState(signedState: string, hmacSecret: string, maxAge?: number, logLabel?: string): Promise<unknown | null>;