@safepassage/sdk 3.0.4 → 3.0.5
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 +2 -7
- package/dist/core/SafePassageSDK.d.ts +158 -5
- package/dist/core/SafePassageSDK.js +237 -42
- package/dist/index.d.ts +19 -115
- package/dist/index.js +8 -2
- package/dist/safepassage.min.js +1 -1
- package/dist/types/index.d.ts +10 -0
- package/dist/utils/__mocks__/polyfills.d.ts +3 -0
- package/dist/utils/__mocks__/polyfills.js +10 -0
- package/dist/utils/crypto.d.ts +80 -6
- package/dist/utils/crypto.js +92 -10
- package/dist/utils/environment.js +19 -11
- package/dist/utils/polyfills.js +20 -17
- package/dist/utils/security.d.ts +1 -1
- package/dist/utils/security.js +33 -21
- package/dist/utils/validation.js +20 -7
- package/package.json +17 -13
|
@@ -1,11 +1,57 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* SafePassage SDK - Redirect-based age verification
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
|
+
* Lightweight SDK for integrating SafePassage age verification using redirect flow.
|
|
5
|
+
* Provides a secure, easy-to-implement solution for age verification with comprehensive
|
|
6
|
+
* security features and flexible integration options.
|
|
7
|
+
*
|
|
8
|
+
* Key Features:
|
|
9
|
+
* - Redirect and new-tab verification modes
|
|
10
|
+
* - Automatic session management for public API keys
|
|
11
|
+
* - HMAC-signed state parameters for security
|
|
12
|
+
* - Rate limiting and race condition protection
|
|
13
|
+
* - Comprehensive security validation and logging
|
|
14
|
+
* - PostMessage communication for new-tab mode
|
|
15
|
+
* - Automatic cleanup and resource management
|
|
16
|
+
* - Environment-specific configuration
|
|
17
|
+
*
|
|
18
|
+
* Security Features:
|
|
19
|
+
* - Origin validation for PostMessage communication
|
|
20
|
+
* - HTTPS enforcement in production
|
|
21
|
+
* - Rate limiting per API key and origin
|
|
22
|
+
* - State parameter signing with timestamps and nonces
|
|
23
|
+
* - Comprehensive security event logging
|
|
24
|
+
* - Protection against race conditions and replay attacks
|
|
25
|
+
*
|
|
26
|
+
* @author SafePassage Engineering
|
|
27
|
+
* @version 2.0.0
|
|
28
|
+
* @since 1.0.0
|
|
4
29
|
*/
|
|
5
30
|
import { generateState, validateConfig } from '../utils/validation';
|
|
6
|
-
import { getEnvironmentUrl, validateEnvironmentSecurity } from '../utils/environment';
|
|
7
|
-
import { validatePostMessageOrigin, validateSafePassageMessage, enforceHTTPS, verificationRateLimit, logSecurityEvent } from '../utils/security';
|
|
31
|
+
import { getEnvironmentUrl, validateEnvironmentSecurity, } from '../utils/environment';
|
|
32
|
+
import { validatePostMessageOrigin, validateSafePassageMessage, enforceHTTPS, verificationRateLimit, logSecurityEvent, } from '../utils/security';
|
|
33
|
+
/**
|
|
34
|
+
* SafePassage SDK Main Class
|
|
35
|
+
*
|
|
36
|
+
* Primary SDK class that manages age verification sessions with comprehensive
|
|
37
|
+
* security and error handling. Supports both redirect and new-tab modes with
|
|
38
|
+
* automatic session management and PostMessage communication.
|
|
39
|
+
*
|
|
40
|
+
* The class implements multiple security layers including origin validation,
|
|
41
|
+
* rate limiting, state signing, and comprehensive event logging to ensure
|
|
42
|
+
* secure verification flows.
|
|
43
|
+
*/
|
|
8
44
|
export class SafePassage {
|
|
45
|
+
/**
|
|
46
|
+
* Initialize SafePassage SDK
|
|
47
|
+
*
|
|
48
|
+
* Validates configuration, sets up security measures, and prepares the SDK
|
|
49
|
+
* for verification operations. Performs comprehensive environment validation
|
|
50
|
+
* and security initialization.
|
|
51
|
+
*
|
|
52
|
+
* @param {SafePassageConfig} config - SDK configuration object
|
|
53
|
+
* @throws {Error} If configuration validation fails
|
|
54
|
+
*/
|
|
9
55
|
constructor(config) {
|
|
10
56
|
this.popupWindow = null;
|
|
11
57
|
this.messageListener = null;
|
|
@@ -17,7 +63,7 @@ export class SafePassage {
|
|
|
17
63
|
this.config = {
|
|
18
64
|
...config,
|
|
19
65
|
environment: config.environment || this.detectEnvironment(),
|
|
20
|
-
mode: config.mode || 'redirect'
|
|
66
|
+
mode: config.mode || 'redirect',
|
|
21
67
|
};
|
|
22
68
|
// Comprehensive environment security validation
|
|
23
69
|
validateEnvironmentSecurity(this.config.environment);
|
|
@@ -29,13 +75,28 @@ export class SafePassage {
|
|
|
29
75
|
mode: this.config.mode,
|
|
30
76
|
origin: window.location.origin,
|
|
31
77
|
protocol: window.location.protocol,
|
|
32
|
-
hostname: window.location.hostname
|
|
78
|
+
hostname: window.location.hostname,
|
|
33
79
|
});
|
|
34
80
|
// Set up automatic cleanup on page unload
|
|
35
81
|
this.setupAutoCleanup();
|
|
36
82
|
}
|
|
37
83
|
/**
|
|
38
84
|
* Initiate age verification with race condition protection
|
|
85
|
+
*
|
|
86
|
+
* Main verification method that handles session creation, security validation,
|
|
87
|
+
* and verification flow initiation. Includes race condition protection and
|
|
88
|
+
* comprehensive error handling.
|
|
89
|
+
*
|
|
90
|
+
* For public keys (pk_*), automatically creates sessions via the portal API.
|
|
91
|
+
* For private keys (sk_*), requires a pre-created sessionId.
|
|
92
|
+
*
|
|
93
|
+
* @param {VerificationOptions} [options={}] - Verification options
|
|
94
|
+
* @param {string} [options.sessionId] - Session ID (required for private keys)
|
|
95
|
+
* @param {number} [options.challengeAge] - Age challenge override
|
|
96
|
+
* @param {string} [options.verificationMode] - Verification mode override
|
|
97
|
+
* @param {string} [options.externalUserId] - External user identifier
|
|
98
|
+
* @returns {Promise<void>} Promise that resolves when verification is initiated
|
|
99
|
+
* @throws {Error} If verification cannot be started or is already in progress
|
|
39
100
|
*/
|
|
40
101
|
async verify(options = {}) {
|
|
41
102
|
const isPublicKey = this.isPublicKey();
|
|
@@ -56,8 +117,10 @@ export class SafePassage {
|
|
|
56
117
|
const error = new Error(`Verification already in progress for session ${this.currentSessionId?.substring(0, 8)}...`);
|
|
57
118
|
logSecurityEvent('RACE_CONDITION_PREVENTED', {
|
|
58
119
|
currentSession: this.currentSessionId?.substring(0, 8) + '...',
|
|
59
|
-
attemptedSession: options.sessionId
|
|
60
|
-
|
|
120
|
+
attemptedSession: options.sessionId
|
|
121
|
+
? options.sessionId.substring(0, 8) + '...'
|
|
122
|
+
: 'undefined',
|
|
123
|
+
origin: window.location.origin,
|
|
61
124
|
});
|
|
62
125
|
this.config.onError?.(error);
|
|
63
126
|
throw error;
|
|
@@ -73,18 +136,23 @@ export class SafePassage {
|
|
|
73
136
|
logSecurityEvent('RATE_LIMIT_EXCEEDED', {
|
|
74
137
|
apiKey: this.config.apiKey.substring(0, 8) + '...',
|
|
75
138
|
origin: window.location.origin,
|
|
76
|
-
sessionId: sessionId
|
|
139
|
+
sessionId: sessionId
|
|
140
|
+
? sessionId.substring(0, 8) + '...'
|
|
141
|
+
: 'undefined',
|
|
77
142
|
});
|
|
78
143
|
this.config.onError?.(error);
|
|
79
144
|
throw error;
|
|
80
145
|
}
|
|
81
|
-
const verificationUrl = await this.buildVerificationUrl({
|
|
146
|
+
const verificationUrl = await this.buildVerificationUrl({
|
|
147
|
+
...options,
|
|
148
|
+
sessionId,
|
|
149
|
+
});
|
|
82
150
|
// Log verification attempt
|
|
83
151
|
logSecurityEvent('VERIFICATION_INITIATED', {
|
|
84
152
|
environment: this.config.environment,
|
|
85
153
|
mode: this.config.mode,
|
|
86
154
|
sessionId: sessionId ? sessionId.substring(0, 8) + '...' : 'undefined',
|
|
87
|
-
origin: window.location.origin
|
|
155
|
+
origin: window.location.origin,
|
|
88
156
|
});
|
|
89
157
|
if (this.config.mode === 'new-tab') {
|
|
90
158
|
this.openNewTab(verificationUrl, sessionId);
|
|
@@ -103,6 +171,14 @@ export class SafePassage {
|
|
|
103
171
|
}
|
|
104
172
|
/**
|
|
105
173
|
* Build verification URL with HMAC-signed state
|
|
174
|
+
*
|
|
175
|
+
* Constructs the verification URL with signed state parameter containing
|
|
176
|
+
* all necessary configuration and security information. The state parameter
|
|
177
|
+
* includes HMAC signature for integrity protection.
|
|
178
|
+
*
|
|
179
|
+
* @param {VerificationOptions} options - Verification options
|
|
180
|
+
* @returns {Promise<string>} Complete verification URL
|
|
181
|
+
* @private
|
|
106
182
|
*/
|
|
107
183
|
async buildVerificationUrl(options) {
|
|
108
184
|
const baseUrl = getEnvironmentUrl(this.config.environment);
|
|
@@ -119,23 +195,48 @@ export class SafePassage {
|
|
|
119
195
|
verificationMode: options.verificationMode || this.config.defaultVerificationMode,
|
|
120
196
|
hasOverrides: hasOverrides, // Flag to indicate explicit overrides
|
|
121
197
|
externalUserId: options.externalUserId,
|
|
122
|
-
timestamp: Date.now()
|
|
198
|
+
timestamp: Date.now(),
|
|
199
|
+
// Phase 2: Include config for self-contained verify-ui
|
|
200
|
+
apiUrl: this.getPortalApiUrl(),
|
|
201
|
+
engineUrl: this.getEngineUrl(),
|
|
202
|
+
wsUrl: this.getWebSocketUrl(),
|
|
203
|
+
environment: this.config.environment,
|
|
204
|
+
features: {
|
|
205
|
+
captureMode: 'enhanced_verification',
|
|
206
|
+
testMode: false,
|
|
207
|
+
warmupPeriodMs: 2000,
|
|
208
|
+
qualityThreshold: 0.6,
|
|
209
|
+
},
|
|
123
210
|
}, this.config.environment);
|
|
124
211
|
const params = new URLSearchParams({
|
|
125
212
|
state,
|
|
126
213
|
sessionId: options.sessionId,
|
|
127
|
-
mode: this.config.mode
|
|
214
|
+
mode: this.config.mode,
|
|
128
215
|
});
|
|
129
|
-
return `${baseUrl}
|
|
216
|
+
return `${baseUrl}/?${params.toString()}`;
|
|
130
217
|
}
|
|
131
218
|
/**
|
|
132
219
|
* Redirect in same tab
|
|
220
|
+
*
|
|
221
|
+
* Performs a full page redirect to the verification URL.
|
|
222
|
+
* Used for redirect mode verification.
|
|
223
|
+
*
|
|
224
|
+
* @param {string} url - Verification URL to redirect to
|
|
225
|
+
* @private
|
|
133
226
|
*/
|
|
134
227
|
redirect(url) {
|
|
135
228
|
window.location.href = url;
|
|
136
229
|
}
|
|
137
230
|
/**
|
|
138
231
|
* Open in new tab with PostMessage communication and proper cleanup
|
|
232
|
+
*
|
|
233
|
+
* Opens verification URL in a new tab/window and sets up secure PostMessage
|
|
234
|
+
* communication for receiving verification results. Includes comprehensive
|
|
235
|
+
* security validation and automatic cleanup.
|
|
236
|
+
*
|
|
237
|
+
* @param {string} url - Verification URL to open
|
|
238
|
+
* @param {string} sessionId - Session ID for result correlation
|
|
239
|
+
* @private
|
|
139
240
|
*/
|
|
140
241
|
openNewTab(url, sessionId) {
|
|
141
242
|
// Clean up any existing resources
|
|
@@ -159,7 +260,7 @@ export class SafePassage {
|
|
|
159
260
|
origin: event.origin,
|
|
160
261
|
environment: this.config.environment,
|
|
161
262
|
expectedOrigins: `SafePassage trusted origins for ${this.config.environment}`,
|
|
162
|
-
messageType: event.data?.type
|
|
263
|
+
messageType: event.data?.type,
|
|
163
264
|
});
|
|
164
265
|
return;
|
|
165
266
|
}
|
|
@@ -170,19 +271,19 @@ export class SafePassage {
|
|
|
170
271
|
error: messageValidation.error,
|
|
171
272
|
origin: event.origin,
|
|
172
273
|
sessionId: sessionId.substring(0, 8) + '...',
|
|
173
|
-
messageType: event.data?.type
|
|
274
|
+
messageType: event.data?.type,
|
|
174
275
|
});
|
|
175
276
|
return;
|
|
176
277
|
}
|
|
177
278
|
const result = {
|
|
178
279
|
sessionId: event.data.sessionId,
|
|
179
|
-
status: event.data.status
|
|
280
|
+
status: event.data.status,
|
|
180
281
|
};
|
|
181
282
|
// Log successful verification completion
|
|
182
283
|
logSecurityEvent('VERIFICATION_COMPLETED', {
|
|
183
284
|
status: result.status,
|
|
184
285
|
sessionId: sessionId.substring(0, 8) + '...',
|
|
185
|
-
origin: event.origin
|
|
286
|
+
origin: event.origin,
|
|
186
287
|
});
|
|
187
288
|
// Clean up resources
|
|
188
289
|
this.cleanup();
|
|
@@ -211,7 +312,7 @@ export class SafePassage {
|
|
|
211
312
|
// Log popup closed event
|
|
212
313
|
logSecurityEvent('POPUP_CLOSED_BY_USER', {
|
|
213
314
|
sessionId: sessionId.substring(0, 8) + '...',
|
|
214
|
-
environment: this.config.environment
|
|
315
|
+
environment: this.config.environment,
|
|
215
316
|
});
|
|
216
317
|
// Clean up and trigger cancel callback
|
|
217
318
|
this.cleanup();
|
|
@@ -223,13 +324,19 @@ export class SafePassage {
|
|
|
223
324
|
}
|
|
224
325
|
/**
|
|
225
326
|
* Set up automatic cleanup on page unload to prevent memory leaks
|
|
327
|
+
*
|
|
328
|
+
* Registers event listeners for page unload events to ensure proper
|
|
329
|
+
* cleanup of resources and verification state. Handles both traditional
|
|
330
|
+
* page navigation and single-page application route changes.
|
|
331
|
+
*
|
|
332
|
+
* @private
|
|
226
333
|
*/
|
|
227
334
|
setupAutoCleanup() {
|
|
228
335
|
this.unloadListener = () => {
|
|
229
336
|
// Log automatic cleanup
|
|
230
337
|
logSecurityEvent('SDK_AUTO_CLEANUP', {
|
|
231
338
|
environment: this.config.environment,
|
|
232
|
-
trigger: 'page_unload'
|
|
339
|
+
trigger: 'page_unload',
|
|
233
340
|
});
|
|
234
341
|
// Clean up all resources and unlock verification
|
|
235
342
|
this.cleanup();
|
|
@@ -250,10 +357,18 @@ export class SafePassage {
|
|
|
250
357
|
}
|
|
251
358
|
/**
|
|
252
359
|
* Auto-detect environment based on current URL
|
|
360
|
+
*
|
|
361
|
+
* Analyzes the current hostname to determine the appropriate environment
|
|
362
|
+
* configuration. Used when environment is not explicitly specified.
|
|
363
|
+
*
|
|
364
|
+
* @returns {'production' | 'staging' | 'development'} Detected environment
|
|
365
|
+
* @private
|
|
253
366
|
*/
|
|
254
367
|
detectEnvironment() {
|
|
255
368
|
const hostname = window.location.hostname;
|
|
256
|
-
if (hostname === 'localhost' ||
|
|
369
|
+
if (hostname === 'localhost' ||
|
|
370
|
+
hostname === '127.0.0.1' ||
|
|
371
|
+
hostname.includes('.local')) {
|
|
257
372
|
return 'development';
|
|
258
373
|
}
|
|
259
374
|
if (hostname.includes('staging') || hostname.includes('stage')) {
|
|
@@ -263,17 +378,27 @@ export class SafePassage {
|
|
|
263
378
|
}
|
|
264
379
|
/**
|
|
265
380
|
* Unlock verification process to allow new verifications
|
|
381
|
+
*
|
|
382
|
+
* Resets the verification lock state to allow new verification attempts.
|
|
383
|
+
* Called after successful completion, errors, or cleanup.
|
|
384
|
+
*
|
|
385
|
+
* @private
|
|
266
386
|
*/
|
|
267
387
|
unlockVerification() {
|
|
268
388
|
this.isVerificationInProgress = false;
|
|
269
389
|
this.currentSessionId = null;
|
|
270
390
|
logSecurityEvent('VERIFICATION_UNLOCKED', {
|
|
271
391
|
environment: this.config.environment,
|
|
272
|
-
origin: window.location.origin
|
|
392
|
+
origin: window.location.origin,
|
|
273
393
|
});
|
|
274
394
|
}
|
|
275
395
|
/**
|
|
276
396
|
* Internal cleanup method to prevent memory leaks
|
|
397
|
+
*
|
|
398
|
+
* Cleans up popup windows, event listeners, and intervals.
|
|
399
|
+
* Does not unlock verification state - that's handled by specific callers.
|
|
400
|
+
*
|
|
401
|
+
* @private
|
|
277
402
|
*/
|
|
278
403
|
cleanup() {
|
|
279
404
|
// Close popup window
|
|
@@ -295,6 +420,10 @@ export class SafePassage {
|
|
|
295
420
|
}
|
|
296
421
|
/**
|
|
297
422
|
* Remove auto-cleanup listeners
|
|
423
|
+
*
|
|
424
|
+
* Removes page unload event listeners that were set up for automatic cleanup.
|
|
425
|
+
*
|
|
426
|
+
* @private
|
|
298
427
|
*/
|
|
299
428
|
removeAutoCleanupListeners() {
|
|
300
429
|
if (this.unloadListener) {
|
|
@@ -305,12 +434,17 @@ export class SafePassage {
|
|
|
305
434
|
}
|
|
306
435
|
/**
|
|
307
436
|
* Public cleanup method for manual resource management
|
|
437
|
+
*
|
|
438
|
+
* Completely destroys the SDK instance, cleaning up all resources and
|
|
439
|
+
* removing all event listeners. Should be called when the SDK is no longer needed.
|
|
440
|
+
*
|
|
441
|
+
* @public
|
|
308
442
|
*/
|
|
309
443
|
destroy() {
|
|
310
444
|
// Log destruction for security monitoring
|
|
311
445
|
logSecurityEvent('SDK_DESTROYED', {
|
|
312
446
|
environment: this.config.environment,
|
|
313
|
-
origin: window.location.origin
|
|
447
|
+
origin: window.location.origin,
|
|
314
448
|
});
|
|
315
449
|
// Clean up all resources
|
|
316
450
|
this.cleanup();
|
|
@@ -319,14 +453,89 @@ export class SafePassage {
|
|
|
319
453
|
// Remove auto-cleanup listeners
|
|
320
454
|
this.removeAutoCleanupListeners();
|
|
321
455
|
}
|
|
456
|
+
/**
|
|
457
|
+
* Get Portal API URL based on environment
|
|
458
|
+
*
|
|
459
|
+
* Returns the appropriate portal-api URL for the current environment.
|
|
460
|
+
*
|
|
461
|
+
* @returns {string} Portal API URL
|
|
462
|
+
* @private
|
|
463
|
+
*/
|
|
464
|
+
getPortalApiUrl() {
|
|
465
|
+
switch (this.config.environment) {
|
|
466
|
+
case 'development':
|
|
467
|
+
return 'http://localhost:3001';
|
|
468
|
+
case 'staging':
|
|
469
|
+
case 'production':
|
|
470
|
+
return 'https://api.safepassageapp.com';
|
|
471
|
+
default:
|
|
472
|
+
return 'https://api.safepassageapp.com';
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
476
|
+
* Get Engine URL based on environment
|
|
477
|
+
*
|
|
478
|
+
* Returns the appropriate verify-engine URL for the current environment.
|
|
479
|
+
* In production, engine access is proxied through portal-api.
|
|
480
|
+
*
|
|
481
|
+
* @returns {string} Engine URL
|
|
482
|
+
* @private
|
|
483
|
+
*/
|
|
484
|
+
getEngineUrl() {
|
|
485
|
+
switch (this.config.environment) {
|
|
486
|
+
case 'development':
|
|
487
|
+
return 'http://localhost:8000';
|
|
488
|
+
case 'staging':
|
|
489
|
+
case 'production':
|
|
490
|
+
// Direct access to verify-engine
|
|
491
|
+
return 'https://engine.safepassageapp.com';
|
|
492
|
+
default:
|
|
493
|
+
return 'https://engine.safepassageapp.com';
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* Get WebSocket URL based on environment
|
|
498
|
+
*
|
|
499
|
+
* Returns the appropriate WebSocket URL for the current environment.
|
|
500
|
+
* In production, WebSocket connections are proxied through portal-api.
|
|
501
|
+
*
|
|
502
|
+
* @returns {string} WebSocket URL
|
|
503
|
+
* @private
|
|
504
|
+
*/
|
|
505
|
+
getWebSocketUrl() {
|
|
506
|
+
switch (this.config.environment) {
|
|
507
|
+
case 'development':
|
|
508
|
+
return 'ws://localhost:8000/safe-passage-llm/api/v1/websocket/stream';
|
|
509
|
+
case 'staging':
|
|
510
|
+
case 'production':
|
|
511
|
+
// Direct WebSocket connection to verify-engine
|
|
512
|
+
return 'wss://engine.safepassageapp.com/safe-passage-llm/api/v1/websocket/stream';
|
|
513
|
+
default:
|
|
514
|
+
return 'wss://engine.safepassageapp.com/safe-passage-llm/api/v1/websocket/stream';
|
|
515
|
+
}
|
|
516
|
+
}
|
|
322
517
|
/**
|
|
323
518
|
* Detect if this is a public key (pk_ prefix) vs private key (sk_ prefix)
|
|
519
|
+
*
|
|
520
|
+
* Determines API key type based on prefix to handle different authentication flows.
|
|
521
|
+
*
|
|
522
|
+
* @returns {boolean} True if public key, false if private key
|
|
523
|
+
* @private
|
|
324
524
|
*/
|
|
325
525
|
isPublicKey() {
|
|
326
526
|
return this.config.apiKey.startsWith('pk_');
|
|
327
527
|
}
|
|
328
528
|
/**
|
|
329
529
|
* Create session internally for public keys
|
|
530
|
+
*
|
|
531
|
+
* Creates a verification session via the portal API for public key authentication.
|
|
532
|
+
* Generates a UUID session ID and submits session creation request with
|
|
533
|
+
* verification parameters.
|
|
534
|
+
*
|
|
535
|
+
* @param {VerificationOptions} options - Verification options
|
|
536
|
+
* @returns {Promise<string>} Created session ID
|
|
537
|
+
* @throws {Error} If session creation fails
|
|
538
|
+
* @private
|
|
330
539
|
*/
|
|
331
540
|
async createInternalSession(options) {
|
|
332
541
|
const sessionId = crypto.randomUUID();
|
|
@@ -336,7 +545,7 @@ export class SafePassage {
|
|
|
336
545
|
method: 'POST',
|
|
337
546
|
headers: {
|
|
338
547
|
'Content-Type': 'application/json',
|
|
339
|
-
|
|
548
|
+
Authorization: `Bearer ${this.config.apiKey}`,
|
|
340
549
|
},
|
|
341
550
|
body: JSON.stringify({
|
|
342
551
|
merchantId: this.config.apiKey, // API expects merchantId even though it uses auth header
|
|
@@ -346,19 +555,19 @@ export class SafePassage {
|
|
|
346
555
|
challengeAge: options.challengeAge,
|
|
347
556
|
verificationMode: options.verificationMode,
|
|
348
557
|
merchantName: document.title || window.location.hostname,
|
|
349
|
-
externalUserId: options.externalUserId
|
|
350
|
-
})
|
|
558
|
+
externalUserId: options.externalUserId,
|
|
559
|
+
}),
|
|
351
560
|
});
|
|
352
561
|
if (!response.ok) {
|
|
353
562
|
const errorData = await response.json().catch(() => ({}));
|
|
354
563
|
throw new Error(`Failed to create session: ${response.status} ${response.statusText}. ${errorData.message || ''}`);
|
|
355
564
|
}
|
|
356
|
-
|
|
565
|
+
await response.json();
|
|
357
566
|
// Log successful session creation
|
|
358
567
|
logSecurityEvent('INTERNAL_SESSION_CREATED', {
|
|
359
568
|
sessionId: sessionId.substring(0, 8) + '...',
|
|
360
569
|
environment: this.config.environment,
|
|
361
|
-
apiKeyType: 'public'
|
|
570
|
+
apiKeyType: 'public',
|
|
362
571
|
});
|
|
363
572
|
return sessionId;
|
|
364
573
|
}
|
|
@@ -367,26 +576,12 @@ export class SafePassage {
|
|
|
367
576
|
logSecurityEvent('INTERNAL_SESSION_FAILED', {
|
|
368
577
|
error: errorMessage,
|
|
369
578
|
environment: this.config.environment,
|
|
370
|
-
apiKeyType: 'public'
|
|
579
|
+
apiKeyType: 'public',
|
|
371
580
|
});
|
|
372
581
|
this.config.onError?.(error);
|
|
373
582
|
throw new Error(`Failed to create verification session: ${errorMessage}`);
|
|
374
583
|
}
|
|
375
584
|
}
|
|
376
|
-
/**
|
|
377
|
-
* Get portal API URL based on environment
|
|
378
|
-
*/
|
|
379
|
-
getPortalApiUrl() {
|
|
380
|
-
switch (this.config.environment) {
|
|
381
|
-
case 'production':
|
|
382
|
-
return 'https://api.safepassageapp.com';
|
|
383
|
-
case 'staging':
|
|
384
|
-
return 'https://api-staging.safepassageapp.com';
|
|
385
|
-
case 'development':
|
|
386
|
-
default:
|
|
387
|
-
return 'http://localhost:3001';
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
585
|
}
|
|
391
586
|
// For backwards compatibility
|
|
392
587
|
export default SafePassage;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,117 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* SafePassage SDK
|
|
2
|
+
* SafePassage SDK - Redirect-based age verification
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```javascript
|
|
6
|
+
* // Initialize SDK
|
|
7
|
+
* const sp = new SafePassage({
|
|
8
|
+
* apiKey: 'pk_live_xxxxx',
|
|
9
|
+
* returnUrl: 'https://merchant.com/verified',
|
|
10
|
+
* cancelUrl: 'https://merchant.com/cancelled'
|
|
11
|
+
* });
|
|
12
|
+
*
|
|
13
|
+
* // Trigger verification
|
|
14
|
+
* sp.verify({
|
|
15
|
+
* sessionId: generateUUID() // Merchant-generated UUID v4
|
|
16
|
+
* });
|
|
17
|
+
* ```
|
|
3
18
|
*/
|
|
4
|
-
export
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
*/
|
|
8
|
-
apiKey: string;
|
|
9
|
-
/**
|
|
10
|
-
* URL to redirect to after successful verification
|
|
11
|
-
* Must be pre-registered in dashboard
|
|
12
|
-
*/
|
|
13
|
-
returnUrl: string;
|
|
14
|
-
/**
|
|
15
|
-
* URL to redirect to if user cancels verification
|
|
16
|
-
* Must be pre-registered in dashboard
|
|
17
|
-
*/
|
|
18
|
-
cancelUrl: string;
|
|
19
|
-
/**
|
|
20
|
-
* Environment to use
|
|
21
|
-
* @default Auto-detected based on hostname
|
|
22
|
-
*/
|
|
23
|
-
environment?: 'production' | 'staging' | 'development';
|
|
24
|
-
/**
|
|
25
|
-
* Verification mode
|
|
26
|
-
* @default 'redirect'
|
|
27
|
-
*/
|
|
28
|
-
mode?: 'redirect' | 'new-tab';
|
|
29
|
-
/**
|
|
30
|
-
* Default challenge age (minimum 25)
|
|
31
|
-
* Can be overridden per verification
|
|
32
|
-
*/
|
|
33
|
-
defaultChallengeAge?: number;
|
|
34
|
-
/**
|
|
35
|
-
* Default verification mode
|
|
36
|
-
* Can be overridden per verification
|
|
37
|
-
*/
|
|
38
|
-
defaultVerificationMode?: 'L1' | 'L2';
|
|
39
|
-
/**
|
|
40
|
-
* Callback when verification completes (new-tab mode only)
|
|
41
|
-
*/
|
|
42
|
-
onComplete?: (result: VerificationResult) => void;
|
|
43
|
-
/**
|
|
44
|
-
* Callback when user cancels (new-tab mode only)
|
|
45
|
-
*/
|
|
46
|
-
onCancel?: () => void;
|
|
47
|
-
/**
|
|
48
|
-
* Callback for errors
|
|
49
|
-
*/
|
|
50
|
-
onError?: (error: Error) => void;
|
|
51
|
-
}
|
|
52
|
-
export interface VerificationOptions {
|
|
53
|
-
/**
|
|
54
|
-
* Merchant-generated UUID v4 for this verification session
|
|
55
|
-
* Required for private keys (sk_), optional for public keys (pk_)
|
|
56
|
-
* For public keys: SDK will generate session internally
|
|
57
|
-
*/
|
|
58
|
-
sessionId?: string;
|
|
59
|
-
/**
|
|
60
|
-
* Minimum age to verify (minimum 25)
|
|
61
|
-
* @default Uses merchant dashboard configuration
|
|
62
|
-
*/
|
|
63
|
-
challengeAge?: number;
|
|
64
|
-
/**
|
|
65
|
-
* Verification mode
|
|
66
|
-
* L1: Age estimation allowed if user appears older
|
|
67
|
-
* L2: Full ID verification required
|
|
68
|
-
* @default Uses merchant dashboard configuration
|
|
69
|
-
*/
|
|
70
|
-
verificationMode?: 'L1' | 'L2';
|
|
71
|
-
}
|
|
72
|
-
export interface VerificationResult {
|
|
73
|
-
/**
|
|
74
|
-
* The session ID that was verified
|
|
75
|
-
*/
|
|
76
|
-
sessionId: string;
|
|
77
|
-
/**
|
|
78
|
-
* Binary result: 'verified' or 'failed'
|
|
79
|
-
* Full details available via server-side API
|
|
80
|
-
*/
|
|
81
|
-
status: 'verified' | 'failed' | 'cancelled';
|
|
82
|
-
}
|
|
83
|
-
export interface StatePayload {
|
|
84
|
-
merchantId: string;
|
|
85
|
-
sessionId: string;
|
|
86
|
-
returnUrl: string;
|
|
87
|
-
cancelUrl: string;
|
|
88
|
-
challengeAge?: number;
|
|
89
|
-
verificationMode?: 'L1' | 'L2';
|
|
90
|
-
hasOverrides?: boolean;
|
|
91
|
-
timestamp: number;
|
|
92
|
-
}
|
|
93
|
-
export interface SessionValidationResponse {
|
|
94
|
-
sessionId: string;
|
|
95
|
-
merchantId: string;
|
|
96
|
-
status: 'verified' | 'failed';
|
|
97
|
-
verified: boolean;
|
|
98
|
-
estimatedAge?: number;
|
|
99
|
-
challengeAge: number;
|
|
100
|
-
verificationMode: 'L1' | 'L2';
|
|
101
|
-
verificationMethod?: 'facial' | 'document' | 'combined';
|
|
102
|
-
timestamp: string;
|
|
103
|
-
expiresAt: string;
|
|
104
|
-
}
|
|
105
|
-
export interface SessionCreationResponse {
|
|
106
|
-
sessionToken: string;
|
|
107
|
-
verifyUrl: string;
|
|
108
|
-
expiresAt: string;
|
|
109
|
-
}
|
|
110
|
-
export interface CreateSessionRequest {
|
|
111
|
-
sessionId: string;
|
|
112
|
-
returnUrl: string;
|
|
113
|
-
cancelUrl?: string;
|
|
114
|
-
challengeAge?: number;
|
|
115
|
-
verificationMode?: 'L1' | 'L2';
|
|
116
|
-
merchantName?: string;
|
|
117
|
-
}
|
|
19
|
+
export { SafePassage, SafePassage as default } from './core/SafePassageSDK';
|
|
20
|
+
export type { SafePassageConfig, VerificationOptions, VerificationResult, SessionValidationResponse, } from './types';
|
|
21
|
+
export declare const VERSION = "3.0.4";
|
package/dist/index.js
CHANGED
|
@@ -28,7 +28,13 @@ export { SafePassage, SafePassage as default } from './core/SafePassageSDK';
|
|
|
28
28
|
export const VERSION = '3.0.4';
|
|
29
29
|
// For UMD builds
|
|
30
30
|
if (typeof window !== 'undefined' && window) {
|
|
31
|
+
// Dynamic import for UMD builds
|
|
32
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
31
33
|
const { SafePassage } = require('./core/SafePassageSDK');
|
|
32
|
-
window
|
|
33
|
-
|
|
34
|
+
// Attach to window object for global access
|
|
35
|
+
const globalWindow = window;
|
|
36
|
+
globalWindow.SafePassage = SafePassage;
|
|
37
|
+
if (globalWindow.SafePassage) {
|
|
38
|
+
globalWindow.SafePassage.VERSION = VERSION;
|
|
39
|
+
}
|
|
34
40
|
}
|