@privateav/sdk 3.5.5 → 3.5.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -13
- package/brands/privateav/index.d.ts +1 -1
- package/core/VerificationSDK.d.ts +3 -17
- package/index.js +127 -343
- package/package.json +1 -1
- package/privateav.min.js +2 -2
- package/sdk.min.js +2 -2
- package/types/base.d.ts +12 -40
- package/utils/validation.d.ts +1 -4
- package/utils/crypto.d.ts +0 -31
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# PrivateAV SDK v3.5.
|
|
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,11 +9,16 @@ 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
|
-
- **
|
|
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
|
+
|
|
17
22
|
### 3.5.5
|
|
18
23
|
- Routes explicit staging sessions through the canonical VerityGuard-owned staging hosts for the selected SDK brand.
|
|
19
24
|
- Keeps production routing unchanged.
|
|
@@ -24,7 +29,7 @@ A lightweight SDK for integrating PrivateAV age verification into your website o
|
|
|
24
29
|
- No public SDK API changes.
|
|
25
30
|
|
|
26
31
|
### 3.5.2
|
|
27
|
-
-
|
|
32
|
+
- Version alignment release across both brand packages at `3.5.2`
|
|
28
33
|
- No SDK API changes from `3.5.0`
|
|
29
34
|
|
|
30
35
|
### 3.5.0
|
|
@@ -40,13 +45,13 @@ A lightweight SDK for integrating PrivateAV age verification into your website o
|
|
|
40
45
|
## Installation
|
|
41
46
|
|
|
42
47
|
```bash
|
|
43
|
-
npm install @privateav/sdk
|
|
48
|
+
npm install @privateav/sdk@3.5.6
|
|
44
49
|
```
|
|
45
50
|
|
|
46
51
|
Or load directly from jsDelivr CDN (no bundler required):
|
|
47
52
|
|
|
48
53
|
```html
|
|
49
|
-
<script src="https://cdn.jsdelivr.net/npm/@privateav/sdk@3.5.
|
|
54
|
+
<script src="https://cdn.jsdelivr.net/npm/@privateav/sdk@3.5.6/sdk.min.js"></script>
|
|
50
55
|
```
|
|
51
56
|
|
|
52
57
|
## Quick Start
|
|
@@ -68,7 +73,7 @@ await sp.verify();
|
|
|
68
73
|
### With CDN (no bundler)
|
|
69
74
|
|
|
70
75
|
```html
|
|
71
|
-
<script src="https://cdn.jsdelivr.net/npm/@privateav/sdk@3.5.
|
|
76
|
+
<script src="https://cdn.jsdelivr.net/npm/@privateav/sdk@3.5.6/sdk.min.js"></script>
|
|
72
77
|
<script>
|
|
73
78
|
const sp = new PrivateAV({
|
|
74
79
|
apiKey: 'pk_...',
|
|
@@ -90,8 +95,6 @@ That's it! The SDK handles session creation automatically.
|
|
|
90
95
|
| cancelUrl | string | No | URL to redirect to if user closes the verification window (new-tab mode) |
|
|
91
96
|
| environment | string | No | `'production'` or `'staging'` (auto-detected) |
|
|
92
97
|
| mode | string | No | `'redirect'` (default) or `'new-tab'` |
|
|
93
|
-
| defaultChallengeAge | number | No | Default minimum age (25 or higher) |
|
|
94
|
-
| defaultVerificationMode | string | No | `'L1'` or `'L2'` |
|
|
95
98
|
| onComplete | function | No | Callback for new-tab mode |
|
|
96
99
|
| onCancel | function | No | Called when user closes popup (new-tab mode). Return `false` to suppress automatic `cancelUrl` redirect. |
|
|
97
100
|
| language | string | No | Default UI language (`'en'`, `'de'`, `'es'`, `'fr'`, `'pt'`, `'it'`). Can be overridden per verification. |
|
|
@@ -103,9 +106,6 @@ Override settings per-verification:
|
|
|
103
106
|
|
|
104
107
|
```javascript
|
|
105
108
|
await sp.verify({
|
|
106
|
-
challengeAge: 30, // Override minimum age for this session
|
|
107
|
-
verificationMode: 'L2', // Force ID verification for this session
|
|
108
|
-
faceMatchEnabled: false, // Disable selfie-vs-ID matching where required
|
|
109
109
|
externalUserId: 'user-123', // Your user ID (returned in webhooks)
|
|
110
110
|
skipIntro: true, // Skip intro screen
|
|
111
111
|
autoReturn: true, // Auto-redirect after success
|
|
@@ -147,7 +147,7 @@ const sp = new PrivateAV({
|
|
|
147
147
|
mode: 'new-tab',
|
|
148
148
|
onComplete: (result) => {
|
|
149
149
|
console.log('Verification complete:', result.sessionId, result.status);
|
|
150
|
-
//
|
|
150
|
+
// Convenience notification only: validate on your server before access
|
|
151
151
|
},
|
|
152
152
|
onCancel: () => {
|
|
153
153
|
console.log('User closed the verification window');
|
|
@@ -226,7 +226,7 @@ For reliable verification tracking, configure webhooks in your dashboard:
|
|
|
226
226
|
<html>
|
|
227
227
|
<head>
|
|
228
228
|
<title>Age Verification</title>
|
|
229
|
-
<script src="https://cdn.jsdelivr.net/npm/@privateav/sdk@3.5.
|
|
229
|
+
<script src="https://cdn.jsdelivr.net/npm/@privateav/sdk@3.5.6/sdk.min.js"></script>
|
|
230
230
|
</head>
|
|
231
231
|
<body>
|
|
232
232
|
<button id="verify-btn">Verify Your Age</button>
|
|
@@ -287,6 +287,7 @@ const config: PrivateAVConfig = {
|
|
|
287
287
|
returnUrl: '/verified',
|
|
288
288
|
mode: 'new-tab',
|
|
289
289
|
onComplete: (result: VerificationResult) => {
|
|
290
|
+
// Convenience notification only; request server-side validation.
|
|
290
291
|
console.log(`Session ${result.sessionId}: ${result.status}`);
|
|
291
292
|
}
|
|
292
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.
|
|
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,30 +17,43 @@ var __spreadValues = (a, b) => {
|
|
|
18
17
|
return a;
|
|
19
18
|
};
|
|
20
19
|
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
20
|
+
|
|
21
|
+
// src-redirect/utils/polyfills.ts
|
|
22
|
+
function setupPolyfills() {
|
|
23
|
+
if (!crypto.randomUUID) {
|
|
24
|
+
crypto.randomUUID = function() {
|
|
25
|
+
const array = new Uint8Array(16);
|
|
26
|
+
crypto.getRandomValues(array);
|
|
27
|
+
array[6] = array[6] & 15 | 64;
|
|
28
|
+
array[8] = array[8] & 63 | 128;
|
|
29
|
+
const hex = Array.from(array).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
30
|
+
return [
|
|
31
|
+
hex.slice(0, 8),
|
|
32
|
+
hex.slice(8, 12),
|
|
33
|
+
hex.slice(12, 16),
|
|
34
|
+
hex.slice(16, 20),
|
|
35
|
+
hex.slice(20, 32)
|
|
36
|
+
].join("-");
|
|
37
|
+
};
|
|
39
38
|
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
};
|
|
39
|
+
}
|
|
40
|
+
function checkBrowserCompatibility(logLabel = "SDK") {
|
|
41
|
+
const warnings = [];
|
|
42
|
+
if (!window.crypto || !window.crypto.getRandomValues) {
|
|
43
|
+
throw new Error(`${logLabel} requires Web Crypto API support`);
|
|
44
|
+
}
|
|
45
|
+
if (!crypto.randomUUID) {
|
|
46
|
+
warnings.push("crypto.randomUUID not supported, using polyfill");
|
|
47
|
+
}
|
|
48
|
+
if (!window.URLSearchParams) {
|
|
49
|
+
warnings.push(
|
|
50
|
+
"URLSearchParams not supported, consider adding a polyfill for IE 11 support"
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
if (warnings.length > 0) {
|
|
54
|
+
console.warn(`${logLabel} Browser Compatibility:`, warnings.join("; "));
|
|
55
|
+
}
|
|
56
|
+
}
|
|
45
57
|
|
|
46
58
|
// src-redirect/utils/security.ts
|
|
47
59
|
function isOriginTrusted(origin, trustedOrigins) {
|
|
@@ -135,6 +147,34 @@ function validateReturnUrl(url, environment, _logLabel = "SDK") {
|
|
|
135
147
|
return { isValid: false, error: "Invalid URL format" };
|
|
136
148
|
}
|
|
137
149
|
}
|
|
150
|
+
var VerificationRateLimit = class {
|
|
151
|
+
constructor() {
|
|
152
|
+
this.attempts = /* @__PURE__ */ new Map();
|
|
153
|
+
this.maxAttempts = 5;
|
|
154
|
+
this.timeWindow = 6e4;
|
|
155
|
+
}
|
|
156
|
+
// 1 minute
|
|
157
|
+
isAllowed(identifier, logLabel = "SDK") {
|
|
158
|
+
const now = Date.now();
|
|
159
|
+
const attempts = this.attempts.get(identifier) || [];
|
|
160
|
+
const recentAttempts = attempts.filter(
|
|
161
|
+
(time) => now - time < this.timeWindow
|
|
162
|
+
);
|
|
163
|
+
if (recentAttempts.length >= this.maxAttempts) {
|
|
164
|
+
console.warn(
|
|
165
|
+
`${logLabel} Security: Rate limit exceeded for ${identifier}`
|
|
166
|
+
);
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
recentAttempts.push(now);
|
|
170
|
+
this.attempts.set(identifier, recentAttempts);
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
reset(identifier) {
|
|
174
|
+
this.attempts.delete(identifier);
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
var verificationRateLimit = new VerificationRateLimit();
|
|
138
178
|
function logSecurityEvent(event, metadata, logLabel = "SDK") {
|
|
139
179
|
const context = {
|
|
140
180
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -143,139 +183,13 @@ function logSecurityEvent(event, metadata, logLabel = "SDK") {
|
|
|
143
183
|
};
|
|
144
184
|
console.warn(`${logLabel} Security Event: ${event}`, __spreadValues(__spreadValues({}, context), metadata));
|
|
145
185
|
}
|
|
146
|
-
var VerificationRateLimit, verificationRateLimit;
|
|
147
|
-
var init_security = __esm({
|
|
148
|
-
"src-redirect/utils/security.ts"() {
|
|
149
|
-
"use strict";
|
|
150
|
-
VerificationRateLimit = class {
|
|
151
|
-
constructor() {
|
|
152
|
-
this.attempts = /* @__PURE__ */ new Map();
|
|
153
|
-
this.maxAttempts = 5;
|
|
154
|
-
this.timeWindow = 6e4;
|
|
155
|
-
}
|
|
156
|
-
// 1 minute
|
|
157
|
-
isAllowed(identifier, logLabel = "SDK") {
|
|
158
|
-
const now = Date.now();
|
|
159
|
-
const attempts = this.attempts.get(identifier) || [];
|
|
160
|
-
const recentAttempts = attempts.filter(
|
|
161
|
-
(time) => now - time < this.timeWindow
|
|
162
|
-
);
|
|
163
|
-
if (recentAttempts.length >= this.maxAttempts) {
|
|
164
|
-
console.warn(
|
|
165
|
-
`${logLabel} Security: Rate limit exceeded for ${identifier}`
|
|
166
|
-
);
|
|
167
|
-
return false;
|
|
168
|
-
}
|
|
169
|
-
recentAttempts.push(now);
|
|
170
|
-
this.attempts.set(identifier, recentAttempts);
|
|
171
|
-
return true;
|
|
172
|
-
}
|
|
173
|
-
reset(identifier) {
|
|
174
|
-
this.attempts.delete(identifier);
|
|
175
|
-
}
|
|
176
|
-
};
|
|
177
|
-
verificationRateLimit = new VerificationRateLimit();
|
|
178
|
-
}
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
// src-redirect/utils/crypto.ts
|
|
182
|
-
var crypto_exports = {};
|
|
183
|
-
__export(crypto_exports, {
|
|
184
|
-
createSignedState: () => createSignedState,
|
|
185
|
-
generateHMAC: () => generateHMAC,
|
|
186
|
-
generateSecureToken: () => generateSecureToken,
|
|
187
|
-
parseSignedState: () => parseSignedState,
|
|
188
|
-
verifyHMAC: () => verifyHMAC
|
|
189
|
-
});
|
|
190
|
-
async function generateHMAC(data, secret) {
|
|
191
|
-
const encoder = new TextEncoder();
|
|
192
|
-
const keyData = encoder.encode(secret);
|
|
193
|
-
const dataBuffer = encoder.encode(data);
|
|
194
|
-
const key = await crypto.subtle.importKey(
|
|
195
|
-
"raw",
|
|
196
|
-
keyData,
|
|
197
|
-
{ name: "HMAC", hash: "SHA-256" },
|
|
198
|
-
false,
|
|
199
|
-
["sign"]
|
|
200
|
-
);
|
|
201
|
-
const signature = await crypto.subtle.sign("HMAC", key, dataBuffer);
|
|
202
|
-
return Array.from(new Uint8Array(signature)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
203
|
-
}
|
|
204
|
-
async function verifyHMAC(data, signature, secret) {
|
|
205
|
-
try {
|
|
206
|
-
const expectedSignature = await generateHMAC(data, secret);
|
|
207
|
-
return constantTimeCompare(signature, expectedSignature);
|
|
208
|
-
} catch (e) {
|
|
209
|
-
return false;
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
function constantTimeCompare(a, b) {
|
|
213
|
-
if (a.length !== b.length) {
|
|
214
|
-
return false;
|
|
215
|
-
}
|
|
216
|
-
let result = 0;
|
|
217
|
-
for (let i = 0; i < a.length; i++) {
|
|
218
|
-
result |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
219
|
-
}
|
|
220
|
-
return result === 0;
|
|
221
|
-
}
|
|
222
|
-
function generateSecureToken(length = 32) {
|
|
223
|
-
const array = new Uint8Array(length);
|
|
224
|
-
crypto.getRandomValues(array);
|
|
225
|
-
return Array.from(array, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
226
|
-
}
|
|
227
|
-
async function createSignedState(payload, hmacSecret) {
|
|
228
|
-
const timestampedPayload = __spreadProps(__spreadValues({}, payload), {
|
|
229
|
-
timestamp: Date.now(),
|
|
230
|
-
nonce: generateSecureToken(16)
|
|
231
|
-
});
|
|
232
|
-
const dataString = JSON.stringify(timestampedPayload);
|
|
233
|
-
const signature = await generateHMAC(dataString, hmacSecret);
|
|
234
|
-
const signedPayload = {
|
|
235
|
-
data: timestampedPayload,
|
|
236
|
-
signature
|
|
237
|
-
};
|
|
238
|
-
return btoa(JSON.stringify(signedPayload));
|
|
239
|
-
}
|
|
240
|
-
async function parseSignedState(signedState, hmacSecret, maxAge = STATE_EXPIRY_MS, logLabel = "SDK") {
|
|
241
|
-
try {
|
|
242
|
-
const json = atob(signedState);
|
|
243
|
-
const signedPayload = JSON.parse(json);
|
|
244
|
-
if (!signedPayload.data || !signedPayload.signature) {
|
|
245
|
-
console.warn(`${logLabel}: Invalid signed state format`);
|
|
246
|
-
return null;
|
|
247
|
-
}
|
|
248
|
-
const { data, signature } = signedPayload;
|
|
249
|
-
const dataString = JSON.stringify(data);
|
|
250
|
-
const isValid = await verifyHMAC(dataString, signature, hmacSecret);
|
|
251
|
-
if (!isValid) {
|
|
252
|
-
console.warn(`${logLabel}: State signature verification failed`);
|
|
253
|
-
return null;
|
|
254
|
-
}
|
|
255
|
-
if (data.timestamp) {
|
|
256
|
-
const age = Date.now() - data.timestamp;
|
|
257
|
-
if (age > maxAge) {
|
|
258
|
-
console.warn(`${logLabel}: State parameter expired`, { age, maxAge });
|
|
259
|
-
return null;
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
const _a = data, { timestamp, nonce } = _a, payload = __objRest(_a, ["timestamp", "nonce"]);
|
|
263
|
-
void timestamp;
|
|
264
|
-
void nonce;
|
|
265
|
-
return payload;
|
|
266
|
-
} catch (error) {
|
|
267
|
-
console.warn(`${logLabel}: Failed to parse signed state`, error);
|
|
268
|
-
return null;
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
var init_crypto = __esm({
|
|
272
|
-
"src-redirect/utils/crypto.ts"() {
|
|
273
|
-
"use strict";
|
|
274
|
-
init_validation();
|
|
275
|
-
}
|
|
276
|
-
});
|
|
277
186
|
|
|
278
187
|
// src-redirect/utils/validation.ts
|
|
188
|
+
var MINIMUM_AGE = 25;
|
|
189
|
+
var MAXIMUM_AGE = 150;
|
|
190
|
+
var MAX_URL_LENGTH = 2048;
|
|
191
|
+
var MAX_API_KEY_LENGTH = 128;
|
|
192
|
+
var API_KEY_PATTERN = /^(pk_|sk_)[a-zA-Z0-9_]+$/;
|
|
279
193
|
function validateConfig(config, context) {
|
|
280
194
|
var _a;
|
|
281
195
|
if (!config.apiKey) {
|
|
@@ -345,68 +259,6 @@ function validateConfig(config, context) {
|
|
|
345
259
|
throw new Error("newTabTarget must be popup or tab");
|
|
346
260
|
}
|
|
347
261
|
}
|
|
348
|
-
async function generateState(payload, environment, hmacSecret, _logLabel = "SDK") {
|
|
349
|
-
const { createSignedState: createSignedState2 } = await Promise.resolve().then(() => (init_crypto(), crypto_exports));
|
|
350
|
-
return createSignedState2(payload, hmacSecret);
|
|
351
|
-
}
|
|
352
|
-
var MINIMUM_AGE, MAXIMUM_AGE, MAX_URL_LENGTH, MAX_API_KEY_LENGTH, STATE_EXPIRY_MS, API_KEY_PATTERN;
|
|
353
|
-
var init_validation = __esm({
|
|
354
|
-
"src-redirect/utils/validation.ts"() {
|
|
355
|
-
"use strict";
|
|
356
|
-
init_security();
|
|
357
|
-
MINIMUM_AGE = 25;
|
|
358
|
-
MAXIMUM_AGE = 150;
|
|
359
|
-
MAX_URL_LENGTH = 2048;
|
|
360
|
-
MAX_API_KEY_LENGTH = 128;
|
|
361
|
-
STATE_EXPIRY_MS = 6e5;
|
|
362
|
-
API_KEY_PATTERN = /^(pk_|sk_)[a-zA-Z0-9_]+$/;
|
|
363
|
-
}
|
|
364
|
-
});
|
|
365
|
-
|
|
366
|
-
// src-redirect/utils/polyfills.ts
|
|
367
|
-
function setupPolyfills() {
|
|
368
|
-
if (!crypto.randomUUID) {
|
|
369
|
-
crypto.randomUUID = function() {
|
|
370
|
-
const array = new Uint8Array(16);
|
|
371
|
-
crypto.getRandomValues(array);
|
|
372
|
-
array[6] = array[6] & 15 | 64;
|
|
373
|
-
array[8] = array[8] & 63 | 128;
|
|
374
|
-
const hex = Array.from(array).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
375
|
-
return [
|
|
376
|
-
hex.slice(0, 8),
|
|
377
|
-
hex.slice(8, 12),
|
|
378
|
-
hex.slice(12, 16),
|
|
379
|
-
hex.slice(16, 20),
|
|
380
|
-
hex.slice(20, 32)
|
|
381
|
-
].join("-");
|
|
382
|
-
};
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
function checkBrowserCompatibility(logLabel = "SDK") {
|
|
386
|
-
const warnings = [];
|
|
387
|
-
if (!window.crypto || !window.crypto.getRandomValues) {
|
|
388
|
-
throw new Error(`${logLabel} requires Web Crypto API support`);
|
|
389
|
-
}
|
|
390
|
-
if (!window.crypto.subtle) {
|
|
391
|
-
throw new Error(
|
|
392
|
-
`${logLabel} requires Web Crypto subtle API for HMAC operations`
|
|
393
|
-
);
|
|
394
|
-
}
|
|
395
|
-
if (!crypto.randomUUID) {
|
|
396
|
-
warnings.push("crypto.randomUUID not supported, using polyfill");
|
|
397
|
-
}
|
|
398
|
-
if (!window.URLSearchParams) {
|
|
399
|
-
warnings.push(
|
|
400
|
-
"URLSearchParams not supported, consider adding a polyfill for IE 11 support"
|
|
401
|
-
);
|
|
402
|
-
}
|
|
403
|
-
if (warnings.length > 0) {
|
|
404
|
-
console.warn(`${logLabel} Browser Compatibility:`, warnings.join("; "));
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
// src-redirect/core/VerificationSDK.ts
|
|
409
|
-
init_validation();
|
|
410
262
|
|
|
411
263
|
// src-redirect/utils/environment.ts
|
|
412
264
|
function getEnvironmentUrl(environment, urls) {
|
|
@@ -525,7 +377,6 @@ function validateEnvironmentSecurity(environment, urls, logLabel = "SDK") {
|
|
|
525
377
|
}
|
|
526
378
|
|
|
527
379
|
// src-redirect/core/VerificationSDK.ts
|
|
528
|
-
init_security();
|
|
529
380
|
var _VerificationSDK = class _VerificationSDK {
|
|
530
381
|
/**
|
|
531
382
|
* Initialize SDK
|
|
@@ -542,16 +393,10 @@ var _VerificationSDK = class _VerificationSDK {
|
|
|
542
393
|
this.isVerificationInProgress = false;
|
|
543
394
|
this.currentSessionId = null;
|
|
544
395
|
this.hasReceivedResult = false;
|
|
545
|
-
// Server-provided verify URL (includes
|
|
396
|
+
// Server-provided verify URL (includes authoritative session context)
|
|
546
397
|
this.lastVerifyUrl = null;
|
|
547
|
-
// Server-provided session token (WS auth)
|
|
548
|
-
this.lastSessionToken = null;
|
|
549
398
|
// External user ID provided during verify() for cancellation redirects
|
|
550
399
|
this.lastExternalUserId = null;
|
|
551
|
-
// Sandbox mode flag from session creation (for UI labeling)
|
|
552
|
-
this.lastSandboxMode = null;
|
|
553
|
-
// Temporary storage for QR handoff token to include in state
|
|
554
|
-
this.temporaryHandoffToken = null;
|
|
555
400
|
this.brandUrls = brandUrls;
|
|
556
401
|
this.brandConstants = brandConstants;
|
|
557
402
|
const environment = resolveEnvironment(config.environment, this.brandUrls);
|
|
@@ -570,6 +415,7 @@ var _VerificationSDK = class _VerificationSDK {
|
|
|
570
415
|
mode: config.mode || "redirect",
|
|
571
416
|
newTabTarget: config.newTabTarget || "popup"
|
|
572
417
|
});
|
|
418
|
+
this.validateServiceOverrides();
|
|
573
419
|
validateEnvironmentSecurity(this.config.environment, this.getUrlConfig(), this.brandConstants.name);
|
|
574
420
|
enforceHTTPS(this.config.environment, this.brandConstants.name);
|
|
575
421
|
logSecurityEvent("SDK_INITIALIZED", {
|
|
@@ -648,82 +494,32 @@ var _VerificationSDK = class _VerificationSDK {
|
|
|
648
494
|
throw error;
|
|
649
495
|
}
|
|
650
496
|
}
|
|
651
|
-
/**
|
|
652
|
-
* Build verification URL with HMAC-signed state
|
|
653
|
-
*/
|
|
497
|
+
/** Build a launch URL from the Portal-issued authoritative verify URL. */
|
|
654
498
|
async buildVerificationUrl(options, sessionId) {
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
const hasExplicitVerificationMode = options.verificationMode !== void 0;
|
|
659
|
-
const hasExplicitFaceMatchEnabled = options.faceMatchEnabled !== void 0 || this.config.faceMatchEnabled !== void 0;
|
|
660
|
-
const hasOverrides = hasExplicitChallengeAge || hasExplicitVerificationMode || hasExplicitFaceMatchEnabled;
|
|
661
|
-
const faceMatchEnabled = (_a = options.faceMatchEnabled) != null ? _a : this.config.faceMatchEnabled;
|
|
662
|
-
const state = await generateState(
|
|
663
|
-
{
|
|
664
|
-
merchantId: this.config.apiKey,
|
|
665
|
-
sessionId,
|
|
666
|
-
returnUrl: this.config.returnUrl,
|
|
667
|
-
cancelUrl: this.config.cancelUrl,
|
|
668
|
-
challengeAge: options.challengeAge || this.config.defaultChallengeAge,
|
|
669
|
-
verificationMode: options.verificationMode || this.config.defaultVerificationMode,
|
|
670
|
-
faceMatchEnabled,
|
|
671
|
-
hasOverrides,
|
|
672
|
-
// Flag to indicate explicit overrides
|
|
673
|
-
externalUserId: options.externalUserId,
|
|
674
|
-
timestamp: Date.now(),
|
|
675
|
-
// Additional config for self-contained verify-ui
|
|
676
|
-
apiUrl: this.getPortalApiUrl(),
|
|
677
|
-
engineUrl: this.getEngineUrl(),
|
|
678
|
-
wsUrl: this.getWebSocketUrl(),
|
|
679
|
-
environment: this.config.environment,
|
|
680
|
-
features: {
|
|
681
|
-
testMode: false,
|
|
682
|
-
warmupPeriodMs: 500,
|
|
683
|
-
qualityThreshold: 0.6,
|
|
684
|
-
sandboxMode: (_b = this.lastSandboxMode) != null ? _b : false
|
|
685
|
-
},
|
|
686
|
-
// Include handoffToken if available (for QR code desktop flow)
|
|
687
|
-
handoffToken: this.temporaryHandoffToken || void 0,
|
|
688
|
-
// Include sessionToken and verifyUrl to make UI auth deterministic
|
|
689
|
-
sessionToken: this.lastSessionToken || void 0,
|
|
690
|
-
verifyUrl: this.lastVerifyUrl || void 0
|
|
691
|
-
},
|
|
692
|
-
this.config.environment,
|
|
693
|
-
this.getHmacSecret(),
|
|
694
|
-
this.brandConstants.name
|
|
695
|
-
);
|
|
499
|
+
if (!this.lastVerifyUrl) {
|
|
500
|
+
throw new Error("Server did not return an authoritative verification URL");
|
|
501
|
+
}
|
|
696
502
|
const language = options.language || this.config.language;
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
url.searchParams.set("state", state);
|
|
702
|
-
url.searchParams.set("mode", this.config.mode);
|
|
703
|
-
if (options.skipIntro) {
|
|
704
|
-
url.searchParams.set("skip_intro", "true");
|
|
705
|
-
}
|
|
706
|
-
if (options.autoReturn) {
|
|
707
|
-
url.searchParams.set("auto_return", "true");
|
|
708
|
-
}
|
|
709
|
-
if (language) {
|
|
710
|
-
url.searchParams.set("lang", language);
|
|
711
|
-
}
|
|
712
|
-
return url.toString();
|
|
713
|
-
} catch (e) {
|
|
714
|
-
}
|
|
503
|
+
const serverUrl = new URL(this.lastVerifyUrl);
|
|
504
|
+
const trustedVerifyOrigin = new URL(this.getUrlConfig().verifyUiUrl).origin;
|
|
505
|
+
if (serverUrl.origin !== trustedVerifyOrigin) {
|
|
506
|
+
throw new Error("Server returned an untrusted verification origin");
|
|
715
507
|
}
|
|
716
|
-
|
|
508
|
+
if (serverUrl.searchParams.get("sessionId") !== sessionId || !serverUrl.searchParams.get("sessionToken")) {
|
|
509
|
+
throw new Error("Server verification URL is missing authoritative session context");
|
|
510
|
+
}
|
|
511
|
+
const url = new URL(this.applyLocalVerifyOverride(serverUrl.toString()));
|
|
512
|
+
url.searchParams.set("mode", this.config.mode);
|
|
717
513
|
if (options.skipIntro) {
|
|
718
|
-
|
|
514
|
+
url.searchParams.set("skip_intro", "true");
|
|
719
515
|
}
|
|
720
516
|
if (options.autoReturn) {
|
|
721
|
-
|
|
517
|
+
url.searchParams.set("auto_return", "true");
|
|
722
518
|
}
|
|
723
519
|
if (language) {
|
|
724
|
-
|
|
520
|
+
url.searchParams.set("lang", language);
|
|
725
521
|
}
|
|
726
|
-
return
|
|
522
|
+
return url.toString();
|
|
727
523
|
}
|
|
728
524
|
/**
|
|
729
525
|
* Redirect in same tab
|
|
@@ -981,22 +777,33 @@ var _VerificationSDK = class _VerificationSDK {
|
|
|
981
777
|
* Get Portal API URL based on environment and brand
|
|
982
778
|
*/
|
|
983
779
|
getPortalApiUrl() {
|
|
984
|
-
|
|
780
|
+
const trustedApiUrl = this.getUrlConfig().apiUrl;
|
|
781
|
+
if (!this.config.apiUrl) {
|
|
782
|
+
return trustedApiUrl;
|
|
783
|
+
}
|
|
784
|
+
const configuredOrigin = this.getUrlOrigin(this.config.apiUrl);
|
|
785
|
+
const trustedOrigin = this.getUrlOrigin(trustedApiUrl);
|
|
786
|
+
if (configuredOrigin === trustedOrigin || this.getLocalOrigin(this.config.apiUrl)) {
|
|
985
787
|
return this.config.apiUrl;
|
|
986
788
|
}
|
|
987
|
-
|
|
789
|
+
throw new Error("apiUrl must use the selected brand/environment service");
|
|
988
790
|
}
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
791
|
+
validateServiceOverrides() {
|
|
792
|
+
const trusted = this.getUrlConfig();
|
|
793
|
+
if (this.config.apiUrl) {
|
|
794
|
+
const configuredOrigin = this.getUrlOrigin(this.config.apiUrl);
|
|
795
|
+
const trustedOrigin = this.getUrlOrigin(trusted.apiUrl);
|
|
796
|
+
if (configuredOrigin !== trustedOrigin && !this.getLocalOrigin(this.config.apiUrl)) {
|
|
797
|
+
throw new Error("apiUrl must use the selected brand/environment service");
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
if (this.config.verifyUrl) {
|
|
801
|
+
const configuredOrigin = this.getUrlOrigin(this.config.verifyUrl);
|
|
802
|
+
const trustedOrigin = this.getUrlOrigin(trusted.verifyUiUrl);
|
|
803
|
+
if (configuredOrigin !== trustedOrigin && !this.getLocalOrigin(this.config.verifyUrl)) {
|
|
804
|
+
throw new Error("verifyUrl must use the selected brand/environment service");
|
|
805
|
+
}
|
|
806
|
+
}
|
|
1000
807
|
}
|
|
1001
808
|
/**
|
|
1002
809
|
* Detect if this is a public key (pk_ prefix) vs private key (sk_ prefix)
|
|
@@ -1008,7 +815,7 @@ var _VerificationSDK = class _VerificationSDK {
|
|
|
1008
815
|
* Create session internally for public keys
|
|
1009
816
|
*/
|
|
1010
817
|
async createInternalSession(options) {
|
|
1011
|
-
var _a, _b
|
|
818
|
+
var _a, _b;
|
|
1012
819
|
try {
|
|
1013
820
|
const portalApiUrl = this.getPortalApiUrl();
|
|
1014
821
|
const response = await fetch(`${portalApiUrl}/api/v1/sessions/create`, {
|
|
@@ -1021,9 +828,6 @@ var _VerificationSDK = class _VerificationSDK {
|
|
|
1021
828
|
merchantId: this.config.apiKey,
|
|
1022
829
|
returnUrl: this.config.returnUrl,
|
|
1023
830
|
cancelUrl: this.config.cancelUrl,
|
|
1024
|
-
challengeAge: options.challengeAge,
|
|
1025
|
-
verificationMode: options.verificationMode,
|
|
1026
|
-
faceMatchEnabled: (_a = options.faceMatchEnabled) != null ? _a : this.config.faceMatchEnabled,
|
|
1027
831
|
merchantName: document.title || window.location.hostname,
|
|
1028
832
|
externalUserId: options.externalUserId
|
|
1029
833
|
})
|
|
@@ -1033,7 +837,7 @@ var _VerificationSDK = class _VerificationSDK {
|
|
|
1033
837
|
const errorCode = errorData == null ? void 0 : errorData.code;
|
|
1034
838
|
if (this.isBillingBlockError(errorCode)) {
|
|
1035
839
|
const language = options.language || this.config.language;
|
|
1036
|
-
this.openBillingBlockPage(errorCode,
|
|
840
|
+
this.openBillingBlockPage(errorCode, language);
|
|
1037
841
|
}
|
|
1038
842
|
throw new Error(
|
|
1039
843
|
`Failed to create session: ${response.status} ${response.statusText}. ${errorData.message || ""}`
|
|
@@ -1047,17 +851,6 @@ var _VerificationSDK = class _VerificationSDK {
|
|
|
1047
851
|
if (sessionData.verifyUrl) {
|
|
1048
852
|
this.lastVerifyUrl = sessionData.verifyUrl;
|
|
1049
853
|
}
|
|
1050
|
-
if (sessionData.sessionToken) {
|
|
1051
|
-
this.lastSessionToken = sessionData.sessionToken;
|
|
1052
|
-
}
|
|
1053
|
-
if (sessionData.handoffToken) {
|
|
1054
|
-
this.temporaryHandoffToken = sessionData.handoffToken;
|
|
1055
|
-
}
|
|
1056
|
-
if (typeof sessionData.sandboxMode === "boolean") {
|
|
1057
|
-
this.lastSandboxMode = sessionData.sandboxMode;
|
|
1058
|
-
} else {
|
|
1059
|
-
this.lastSandboxMode = null;
|
|
1060
|
-
}
|
|
1061
854
|
logSecurityEvent("INTERNAL_SESSION_CREATED", {
|
|
1062
855
|
sessionId: sessionId.substring(0, 8) + "...",
|
|
1063
856
|
environment: this.config.environment,
|
|
@@ -1071,23 +864,20 @@ var _VerificationSDK = class _VerificationSDK {
|
|
|
1071
864
|
environment: this.config.environment,
|
|
1072
865
|
apiKeyType: "public"
|
|
1073
866
|
}, this.brandConstants.name);
|
|
1074
|
-
(
|
|
867
|
+
(_b = (_a = this.config).onError) == null ? void 0 : _b.call(_a, error);
|
|
1075
868
|
throw new Error(`Failed to create verification session: ${errorMessage}`);
|
|
1076
869
|
}
|
|
1077
870
|
}
|
|
1078
871
|
isBillingBlockError(code) {
|
|
1079
872
|
return code === "SUBSCRIPTION_REQUIRED" || code === "PLAN_LIMIT_REACHED" || code === "SANDBOX_LIMIT_REACHED";
|
|
1080
873
|
}
|
|
1081
|
-
openBillingBlockPage(code,
|
|
874
|
+
openBillingBlockPage(code, language) {
|
|
1082
875
|
var _a, _b, _c, _d;
|
|
1083
876
|
try {
|
|
1084
|
-
const baseUrl =
|
|
877
|
+
const baseUrl = getEnvironmentUrl(this.config.environment, this.getUrlConfig());
|
|
1085
878
|
const resolvedUrl = this.applyLocalVerifyOverride(baseUrl);
|
|
1086
879
|
const url = new URL(resolvedUrl);
|
|
1087
880
|
url.searchParams.set("blocked", code);
|
|
1088
|
-
if (portalUrl) {
|
|
1089
|
-
url.searchParams.set("portalUrl", portalUrl);
|
|
1090
|
-
}
|
|
1091
881
|
const effectiveLanguage = language || this.config.language;
|
|
1092
882
|
if (effectiveLanguage) {
|
|
1093
883
|
url.searchParams.set("lang", effectiveLanguage);
|
|
@@ -1150,6 +940,13 @@ var _VerificationSDK = class _VerificationSDK {
|
|
|
1150
940
|
}
|
|
1151
941
|
return null;
|
|
1152
942
|
}
|
|
943
|
+
getUrlOrigin(urlValue) {
|
|
944
|
+
try {
|
|
945
|
+
return new URL(urlValue).origin;
|
|
946
|
+
} catch (e) {
|
|
947
|
+
return null;
|
|
948
|
+
}
|
|
949
|
+
}
|
|
1153
950
|
applyLocalVerifyOverride(rawUrl) {
|
|
1154
951
|
const overrideOrigin = this.getLocalOrigin(this.config.verifyUrl || null);
|
|
1155
952
|
if (!overrideOrigin) {
|
|
@@ -1165,9 +962,6 @@ var _VerificationSDK = class _VerificationSDK {
|
|
|
1165
962
|
return rawUrl;
|
|
1166
963
|
}
|
|
1167
964
|
}
|
|
1168
|
-
getHmacSecret() {
|
|
1169
|
-
return this.config.environment === "staging" ? this.brandConstants.hmacSecretStaging : this.brandConstants.hmacSecretProd;
|
|
1170
|
-
}
|
|
1171
965
|
};
|
|
1172
966
|
// Local override hostnames allowed for internal testing
|
|
1173
967
|
_VerificationSDK.LOCAL_HOSTNAMES = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "::1"]);
|
|
@@ -1180,28 +974,18 @@ var BRAND_URLS = {
|
|
|
1180
974
|
verifyUiUrl: "https://verify.privateav.com",
|
|
1181
975
|
engineUrl: "https://engine.privateav.com",
|
|
1182
976
|
wsUrl: "wss://engine.privateav.com/api/websocket/stream",
|
|
1183
|
-
trustedOrigins: [
|
|
1184
|
-
"https://verify.privateav.com",
|
|
1185
|
-
"https://portal.privateav.com",
|
|
1186
|
-
"https://api.privateav.com"
|
|
1187
|
-
]
|
|
977
|
+
trustedOrigins: ["https://verify.privateav.com"]
|
|
1188
978
|
},
|
|
1189
979
|
staging: {
|
|
1190
980
|
apiUrl: "https://api.staging.privateav.com",
|
|
1191
981
|
verifyUiUrl: "https://verify.staging.privateav.com",
|
|
1192
982
|
engineUrl: "https://engine.staging.privateav.com",
|
|
1193
983
|
wsUrl: "wss://engine.staging.privateav.com/api/websocket/stream",
|
|
1194
|
-
trustedOrigins: [
|
|
1195
|
-
"https://verify.staging.privateav.com",
|
|
1196
|
-
"https://portal.staging.privateav.com",
|
|
1197
|
-
"https://api.staging.privateav.com"
|
|
1198
|
-
]
|
|
984
|
+
trustedOrigins: ["https://verify.staging.privateav.com"]
|
|
1199
985
|
}
|
|
1200
986
|
};
|
|
1201
987
|
var BRAND_CONSTANTS = {
|
|
1202
988
|
name: "PrivateAV",
|
|
1203
|
-
hmacSecretProd: "privateav-prod-hmac-2025",
|
|
1204
|
-
hmacSecretStaging: "privateav-stage-hmac-2025",
|
|
1205
989
|
messageType: "privateav:verification:complete",
|
|
1206
990
|
legacyMessageType: "privateav-verification",
|
|
1207
991
|
popupName: "privateav-verify",
|
|
@@ -1214,7 +998,7 @@ var PrivateAV = class extends VerificationSDK {
|
|
|
1214
998
|
super(config, BRAND_URLS, BRAND_CONSTANTS);
|
|
1215
999
|
}
|
|
1216
1000
|
};
|
|
1217
|
-
var VERSION = "3.5.
|
|
1001
|
+
var VERSION = "3.5.6";
|
|
1218
1002
|
PrivateAV.VERSION = VERSION;
|
|
1219
1003
|
if (typeof window !== "undefined") {
|
|
1220
1004
|
setupPolyfills();
|
package/package.json
CHANGED
package/privateav.min.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
/* PrivateAV SDK v3.5.
|
|
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,w=(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,n)=>()=>{if(n)throw n[0];try{return t&&(e=t(t=0)),e}catch(r){throw n=[r],r}};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 we=t=>me(S({},"__esModule",{value:!0}),t);function ve(t,e){return e.includes(t)}function G(t,e,n=[],r="SDK"){var o;let{origin:i}=t;return ve(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:(o=t.data)==null?void 0:o.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 o of i)if(o.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}`,w(w({},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(),o=(this.attempts.get(e)||[]).filter(l=>r-l<this.timeWindow);return o.length>=this.maxAttempts?(console.warn(`${n} Security: Rate limit exceeded for ${e}`),!1):(o.push(r),this.attempts.set(e,o),!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),o=await crypto.subtle.importKey("raw",r,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),l=await crypto.subtle.sign("HMAC",o,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(w({},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 o=atob(t),l=JSON.parse(o);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(o){return console.warn(`${r}: Failed to parse signed state`,o),null}}var ee=A(()=>{"use strict";V()});function oe(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 o=x(t.cancelUrl,n,e.brandName);if(!o.isValid)throw new Error(`cancelUrl validation failed: ${o.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 se(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:()=>v,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 o,l;if(!t||!t.staging)return[];let e=t.staging,n=ae([e.apiUrl,e.verifyUiUrl,e.engineUrl,e.wsUrl,...(o=e.trustedOrigins)!=null?o:[]]),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 o=i instanceof Error?i.message:String(i);throw new Error(`Environment configuration validation failed: ${o}`)}}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'`),oe(e,{brandName:this.brandConstants.name,docsUrl:this.brandConstants.docsUrl,environment:i}),this.config=U(w({},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,o,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 s=new Error(`Verification already in progress for session ${(i=this.currentSessionId)==null?void 0:i.substring(0,8)}...`);throw p("RACE_CONDITION_PREVENTED",{currentSession:((o=this.currentSessionId)==null?void 0:o.substring(0,8))+"...",attemptedSession:"new-session-attempt",origin:window.location.origin},this.brandConstants.name),(a=(l=this.config).onError)==null||a.call(l,s),s}this.isVerificationInProgress=!0,this.currentSessionId=r,this.lastExternalUserId=e.externalUserId||null;try{let s=`${this.config.apiKey}:${window.location.origin}`;if(!Y.isAllowed(s,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(s){throw this.unlockVerification(),s}}async buildVerificationUrl(e,n){var f,m;let r=this.config.verifyUrl||E(this.config.environment,this.getUrlConfig()),i=e.challengeAge!==void 0,o=e.verificationMode!==void 0,l=e.faceMatchEnabled!==void 0||this.config.faceMatchEnabled!==void 0,a=i||o||l,c=(f=e.faceMatchEnabled)!=null?f:this.config.faceMatchEnabled,d=await se({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),s=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"),s&&u.searchParams.set("lang",s),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"),s&&g.set("lang",s),`${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(),o=this.getAllowedCustomOrigins(e),l=this.brandConstants.messageType,a=this.brandConstants.legacyMessageType;this.messageListener=s=>{var k,D,_,O,$,N,K;let g=(k=s.data)==null?void 0:k.type;if(!g||typeof g!="string"||!(a?[l,a]:[l]).includes(g))return;if(!G(s,i,o,this.brandConstants.name)){p("POSTMESSAGE_ORIGIN_BLOCKED",{origin:s.origin,environment:this.config.environment,expectedOrigins:`${this.brandConstants.name} trusted origins for ${this.config.environment}`,messageType:(D=s.data)==null?void 0:D.type},this.brandConstants.name);return}let m=J(s,n,l,a);if(!m.isValid){p("POSTMESSAGE_VALIDATION_FAILED",{error:m.error,origin:s.origin,sessionId:n.substring(0,8)+"...",messageType:(_=s.data)==null?void 0:_.type},this.brandConstants.name);return}let h=s.data.status;if(h==="cancelled"){this.handleCancellation(n,"postmessage");return}let u={sessionId:s.data.sessionId,status:h,timestamp:s.data.timestamp,externalUserId:s.data.externalUserId};this.hasReceivedResult=!0,p("VERIFICATION_COMPLETED",{status:u.status,sessionId:n.substring(0,8)+"...",origin:s.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 o=this.getPortalApiUrl(),l=await fetch(`${o}/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(()=>({})),s=d==null?void 0:d.code;if(this.isBillingBlockError(s)){let g=e.language||this.config.language;this.openBillingBlockPage(s,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(o){let l=o instanceof Error?o.message:String(o);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,o),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,o,l,a;try{let c=this.config.verifyUrl||E(this.config.environment,this.getUrlConfig()),d=this.applyLocalVerifyOverride(c),s=new URL(d);s.searchParams.set("blocked",e),n&&s.searchParams.set("portalUrl",n);let g=r||this.config.language;if(g&&s.searchParams.set("lang",g),this.config.mode==="new-tab"){((this.config.newTabTarget||"popup")==="tab"?window.open(s.toString(),"_blank"):window.open(s.toString(),this.brandConstants.popupName,"width=600,height=700"))||(o=(i=this.config).onError)==null||o.call(i,new Error("Failed to open billing notice window. Please check popup blocker settings."));return}this.redirect(s.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.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","https://portal.staging.privateav.com","https://api.staging.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 v=class extends C{constructor(e){super(e,de,L)}},ge="3.5.5";v.VERSION=ge;typeof window!="undefined"&&(j(),q(`${L.name} SDK`));var Pe=v;return we(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.
|
|
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,w=(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,n)=>()=>{if(n)throw n[0];try{return t&&(e=t(t=0)),e}catch(r){throw n=[r],r}};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 we=t=>me(S({},"__esModule",{value:!0}),t);function ve(t,e){return e.includes(t)}function G(t,e,n=[],r="SDK"){var o;let{origin:i}=t;return ve(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:(o=t.data)==null?void 0:o.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 o of i)if(o.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}`,w(w({},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(),o=(this.attempts.get(e)||[]).filter(l=>r-l<this.timeWindow);return o.length>=this.maxAttempts?(console.warn(`${n} Security: Rate limit exceeded for ${e}`),!1):(o.push(r),this.attempts.set(e,o),!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),o=await crypto.subtle.importKey("raw",r,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),l=await crypto.subtle.sign("HMAC",o,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(w({},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 o=atob(t),l=JSON.parse(o);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(o){return console.warn(`${r}: Failed to parse signed state`,o),null}}var ee=A(()=>{"use strict";V()});function oe(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 o=x(t.cancelUrl,n,e.brandName);if(!o.isValid)throw new Error(`cancelUrl validation failed: ${o.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 se(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:()=>v,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 o,l;if(!t||!t.staging)return[];let e=t.staging,n=ae([e.apiUrl,e.verifyUiUrl,e.engineUrl,e.wsUrl,...(o=e.trustedOrigins)!=null?o:[]]),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 o=i instanceof Error?i.message:String(i);throw new Error(`Environment configuration validation failed: ${o}`)}}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'`),oe(e,{brandName:this.brandConstants.name,docsUrl:this.brandConstants.docsUrl,environment:i}),this.config=U(w({},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,o,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 s=new Error(`Verification already in progress for session ${(i=this.currentSessionId)==null?void 0:i.substring(0,8)}...`);throw p("RACE_CONDITION_PREVENTED",{currentSession:((o=this.currentSessionId)==null?void 0:o.substring(0,8))+"...",attemptedSession:"new-session-attempt",origin:window.location.origin},this.brandConstants.name),(a=(l=this.config).onError)==null||a.call(l,s),s}this.isVerificationInProgress=!0,this.currentSessionId=r,this.lastExternalUserId=e.externalUserId||null;try{let s=`${this.config.apiKey}:${window.location.origin}`;if(!Y.isAllowed(s,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(s){throw this.unlockVerification(),s}}async buildVerificationUrl(e,n){var f,m;let r=this.config.verifyUrl||E(this.config.environment,this.getUrlConfig()),i=e.challengeAge!==void 0,o=e.verificationMode!==void 0,l=e.faceMatchEnabled!==void 0||this.config.faceMatchEnabled!==void 0,a=i||o||l,c=(f=e.faceMatchEnabled)!=null?f:this.config.faceMatchEnabled,d=await se({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),s=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"),s&&u.searchParams.set("lang",s),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"),s&&g.set("lang",s),`${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(),o=this.getAllowedCustomOrigins(e),l=this.brandConstants.messageType,a=this.brandConstants.legacyMessageType;this.messageListener=s=>{var k,D,_,O,$,N,K;let g=(k=s.data)==null?void 0:k.type;if(!g||typeof g!="string"||!(a?[l,a]:[l]).includes(g))return;if(!G(s,i,o,this.brandConstants.name)){p("POSTMESSAGE_ORIGIN_BLOCKED",{origin:s.origin,environment:this.config.environment,expectedOrigins:`${this.brandConstants.name} trusted origins for ${this.config.environment}`,messageType:(D=s.data)==null?void 0:D.type},this.brandConstants.name);return}let m=J(s,n,l,a);if(!m.isValid){p("POSTMESSAGE_VALIDATION_FAILED",{error:m.error,origin:s.origin,sessionId:n.substring(0,8)+"...",messageType:(_=s.data)==null?void 0:_.type},this.brandConstants.name);return}let h=s.data.status;if(h==="cancelled"){this.handleCancellation(n,"postmessage");return}let u={sessionId:s.data.sessionId,status:h,timestamp:s.data.timestamp,externalUserId:s.data.externalUserId};this.hasReceivedResult=!0,p("VERIFICATION_COMPLETED",{status:u.status,sessionId:n.substring(0,8)+"...",origin:s.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 o=this.getPortalApiUrl(),l=await fetch(`${o}/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(()=>({})),s=d==null?void 0:d.code;if(this.isBillingBlockError(s)){let g=e.language||this.config.language;this.openBillingBlockPage(s,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(o){let l=o instanceof Error?o.message:String(o);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,o),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,o,l,a;try{let c=this.config.verifyUrl||E(this.config.environment,this.getUrlConfig()),d=this.applyLocalVerifyOverride(c),s=new URL(d);s.searchParams.set("blocked",e),n&&s.searchParams.set("portalUrl",n);let g=r||this.config.language;if(g&&s.searchParams.set("lang",g),this.config.mode==="new-tab"){((this.config.newTabTarget||"popup")==="tab"?window.open(s.toString(),"_blank"):window.open(s.toString(),this.brandConstants.popupName,"width=600,height=700"))||(o=(i=this.config).onError)==null||o.call(i,new Error("Failed to open billing notice window. Please check popup blocker settings."));return}this.redirect(s.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.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","https://portal.staging.privateav.com","https://api.staging.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 v=class extends C{constructor(e){super(e,de,L)}},ge="3.5.5";v.VERSION=ge;typeof window!="undefined"&&(j(),q(`${L.name} SDK`));var Pe=v;return we(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
|
-
*
|
|
36
|
-
*
|
|
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
|
-
*
|
|
41
|
-
*
|
|
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
|
-
*
|
|
46
|
-
*
|
|
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
|
-
*
|
|
80
|
-
*
|
|
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
|
-
*
|
|
85
|
-
*
|
|
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
|
-
*
|
|
92
|
-
*
|
|
89
|
+
* @deprecated Browser SDK policy overrides are ignored. Configure verification
|
|
90
|
+
* policy server-side.
|
|
93
91
|
*/
|
|
94
92
|
faceMatchEnabled?: boolean;
|
|
95
93
|
/**
|
|
@@ -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;
|
package/utils/validation.d.ts
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Validation utilities for the SDK
|
|
3
3
|
*/
|
|
4
|
-
import type { SDKConfig
|
|
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>;
|