@safepassage/sdk 3.1.4 → 3.2.1
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 +7 -7
- package/dist/core/SafePassageSDK.js +23 -22
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/safepassage.min.js +2 -2
- package/dist/types/index.d.ts +1 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -30,7 +30,7 @@ npm install @safepassage/sdk
|
|
|
30
30
|
```javascript
|
|
31
31
|
// Initialize SDK
|
|
32
32
|
const safePassage = new SafePassage({
|
|
33
|
-
apiKey: '
|
|
33
|
+
apiKey: 'sk_xxxxx', // Your API key
|
|
34
34
|
returnUrl: 'https://yoursite.com/verified',
|
|
35
35
|
cancelUrl: 'https://yoursite.com/cancelled'
|
|
36
36
|
});
|
|
@@ -126,7 +126,7 @@ User is redirected to SafePassage, then back to your site:
|
|
|
126
126
|
|
|
127
127
|
```javascript
|
|
128
128
|
const safePassage = new SafePassage({
|
|
129
|
-
apiKey: '
|
|
129
|
+
apiKey: 'sk_xxxxx',
|
|
130
130
|
returnUrl: '/age-verified',
|
|
131
131
|
cancelUrl: '/age-gate'
|
|
132
132
|
});
|
|
@@ -139,7 +139,7 @@ Verification opens in a popup window:
|
|
|
139
139
|
|
|
140
140
|
```javascript
|
|
141
141
|
const safePassage = new SafePassage({
|
|
142
|
-
apiKey: '
|
|
142
|
+
apiKey: 'sk_xxxxx',
|
|
143
143
|
returnUrl: '/age-verified',
|
|
144
144
|
cancelUrl: '/age-gate',
|
|
145
145
|
mode: 'new-tab',
|
|
@@ -164,7 +164,7 @@ Always validate the session on your server:
|
|
|
164
164
|
const response = await fetch('https://api.safepassageapp.com/v1/sessions/validate', {
|
|
165
165
|
method: 'POST',
|
|
166
166
|
headers: {
|
|
167
|
-
'Authorization': 'Bearer
|
|
167
|
+
'Authorization': 'Bearer sk_xxxxx', // Secret key
|
|
168
168
|
'Content-Type': 'application/json'
|
|
169
169
|
},
|
|
170
170
|
body: JSON.stringify({ sessionId })
|
|
@@ -208,7 +208,7 @@ function generateUUID() {
|
|
|
208
208
|
|
|
209
209
|
<script>
|
|
210
210
|
const safePassage = new SafePassage({
|
|
211
|
-
apiKey: '
|
|
211
|
+
apiKey: 'sk_xxxxx',
|
|
212
212
|
returnUrl: window.location.href + '?verified=true',
|
|
213
213
|
cancelUrl: window.location.href
|
|
214
214
|
});
|
|
@@ -260,7 +260,7 @@ const safePassage = new SafePassage(config);
|
|
|
260
260
|
New streamlined approach (10 lines):
|
|
261
261
|
```javascript
|
|
262
262
|
const safePassage = new SafePassage({
|
|
263
|
-
apiKey: '
|
|
263
|
+
apiKey: 'sk_xxxxx',
|
|
264
264
|
returnUrl: '/verified',
|
|
265
265
|
cancelUrl: '/cancelled'
|
|
266
266
|
});
|
|
@@ -280,4 +280,4 @@ safePassage.verify({ sessionId: crypto.randomUUID() });
|
|
|
280
280
|
1. Always generate session IDs on the merchant side
|
|
281
281
|
2. Validate sessions server-side before granting access
|
|
282
282
|
3. Pre-register callback URLs in your dashboard
|
|
283
|
-
4. Never expose your secret key (
|
|
283
|
+
4. Never expose your secret key (sk_xxx)
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
*/
|
|
30
30
|
import { generateState, validateConfig } from '../utils/validation';
|
|
31
31
|
import { getEnvironmentUrl, validateEnvironmentSecurity, } from '../utils/environment';
|
|
32
|
-
import { validatePostMessageOrigin, validateSafePassageMessage, enforceHTTPS, verificationRateLimit, logSecurityEvent,
|
|
32
|
+
import { validatePostMessageOrigin, validateSafePassageMessage, enforceHTTPS, verificationRateLimit, logSecurityEvent, } from '../utils/security';
|
|
33
33
|
/**
|
|
34
34
|
* SafePassage SDK Main Class
|
|
35
35
|
*
|
|
@@ -106,26 +106,25 @@ export class SafePassage {
|
|
|
106
106
|
*/
|
|
107
107
|
async verify(options = {}) {
|
|
108
108
|
const isPublicKey = this.isPublicKey();
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
if (isPublicKey
|
|
109
|
+
// For public keys, create session via API (SafePassage generates the sessionId)
|
|
110
|
+
let sessionId;
|
|
111
|
+
if (isPublicKey) {
|
|
112
112
|
sessionId = await this.createInternalSession(options);
|
|
113
113
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
throw new Error('
|
|
114
|
+
else {
|
|
115
|
+
// Private keys should use the direct API, not the SDK
|
|
116
|
+
throw new Error('Private API keys (sk_) should use the direct API, not the SDK. ' +
|
|
117
|
+
'The SDK is designed for browser-based public key usage only.');
|
|
117
118
|
}
|
|
118
119
|
if (!sessionId) {
|
|
119
|
-
throw new Error('Failed to
|
|
120
|
+
throw new Error('Failed to obtain sessionId from server');
|
|
120
121
|
}
|
|
121
122
|
// Race condition check - prevent multiple simultaneous verifications
|
|
122
123
|
if (this.isVerificationInProgress) {
|
|
123
124
|
const error = new Error(`Verification already in progress for session ${this.currentSessionId?.substring(0, 8)}...`);
|
|
124
125
|
logSecurityEvent('RACE_CONDITION_PREVENTED', {
|
|
125
126
|
currentSession: this.currentSessionId?.substring(0, 8) + '...',
|
|
126
|
-
attemptedSession:
|
|
127
|
-
? options.sessionId.substring(0, 8) + '...'
|
|
128
|
-
: 'undefined',
|
|
127
|
+
attemptedSession: 'new-session-attempt',
|
|
129
128
|
origin: window.location.origin,
|
|
130
129
|
});
|
|
131
130
|
this.config.onError?.(error);
|
|
@@ -149,10 +148,7 @@ export class SafePassage {
|
|
|
149
148
|
this.config.onError?.(error);
|
|
150
149
|
throw error;
|
|
151
150
|
}
|
|
152
|
-
const verificationUrl = await this.buildVerificationUrl(
|
|
153
|
-
...options,
|
|
154
|
-
sessionId,
|
|
155
|
-
});
|
|
151
|
+
const verificationUrl = await this.buildVerificationUrl(options, sessionId);
|
|
156
152
|
// Log verification attempt
|
|
157
153
|
logSecurityEvent('VERIFICATION_INITIATED', {
|
|
158
154
|
environment: this.config.environment,
|
|
@@ -164,8 +160,10 @@ export class SafePassage {
|
|
|
164
160
|
this.openNewTab(verificationUrl, sessionId);
|
|
165
161
|
}
|
|
166
162
|
else {
|
|
167
|
-
// For redirect mode,
|
|
168
|
-
|
|
163
|
+
// For redirect mode, navigate immediately so any page-level
|
|
164
|
+
// loading indicator remains visible until unload. We intentionally
|
|
165
|
+
// do not unlock here to avoid a brief flash of original content
|
|
166
|
+
// before the browser leaves the page.
|
|
169
167
|
this.redirect(verificationUrl);
|
|
170
168
|
}
|
|
171
169
|
}
|
|
@@ -186,7 +184,7 @@ export class SafePassage {
|
|
|
186
184
|
* @returns {Promise<string>} Complete verification URL
|
|
187
185
|
* @private
|
|
188
186
|
*/
|
|
189
|
-
async buildVerificationUrl(options) {
|
|
187
|
+
async buildVerificationUrl(options, sessionId) {
|
|
190
188
|
const baseUrl = getEnvironmentUrl(this.config.environment);
|
|
191
189
|
// Determine if we have explicit overrides
|
|
192
190
|
const hasExplicitChallengeAge = options.challengeAge !== undefined;
|
|
@@ -194,7 +192,7 @@ export class SafePassage {
|
|
|
194
192
|
const hasOverrides = hasExplicitChallengeAge || hasExplicitVerificationMode;
|
|
195
193
|
const state = await generateState({
|
|
196
194
|
merchantId: this.config.apiKey,
|
|
197
|
-
sessionId
|
|
195
|
+
sessionId,
|
|
198
196
|
returnUrl: this.config.returnUrl,
|
|
199
197
|
cancelUrl: this.config.cancelUrl,
|
|
200
198
|
challengeAge: options.challengeAge || this.config.defaultChallengeAge,
|
|
@@ -217,7 +215,7 @@ export class SafePassage {
|
|
|
217
215
|
}, this.config.environment);
|
|
218
216
|
const params = new URLSearchParams({
|
|
219
217
|
state,
|
|
220
|
-
sessionId
|
|
218
|
+
sessionId,
|
|
221
219
|
mode: this.config.mode,
|
|
222
220
|
});
|
|
223
221
|
return `${baseUrl}/?${params.toString()}`;
|
|
@@ -545,7 +543,6 @@ export class SafePassage {
|
|
|
545
543
|
* @private
|
|
546
544
|
*/
|
|
547
545
|
async createInternalSession(options) {
|
|
548
|
-
const sessionId = generateSecureSessionId();
|
|
549
546
|
try {
|
|
550
547
|
const portalApiUrl = this.getPortalApiUrl();
|
|
551
548
|
const response = await fetch(`${portalApiUrl}/api/v1/sessions/create`, {
|
|
@@ -556,7 +553,6 @@ export class SafePassage {
|
|
|
556
553
|
},
|
|
557
554
|
body: JSON.stringify({
|
|
558
555
|
merchantId: this.config.apiKey, // API expects merchantId even though it uses auth header
|
|
559
|
-
sessionId,
|
|
560
556
|
returnUrl: this.config.returnUrl,
|
|
561
557
|
cancelUrl: this.config.cancelUrl,
|
|
562
558
|
challengeAge: options.challengeAge,
|
|
@@ -570,6 +566,11 @@ export class SafePassage {
|
|
|
570
566
|
throw new Error(`Failed to create session: ${response.status} ${response.statusText}. ${errorData.message || ''}`);
|
|
571
567
|
}
|
|
572
568
|
const sessionData = await response.json();
|
|
569
|
+
// SafePassage now generates the sessionId internally
|
|
570
|
+
const sessionId = sessionData.sessionId;
|
|
571
|
+
if (!sessionId) {
|
|
572
|
+
throw new Error('Server did not return a sessionId');
|
|
573
|
+
}
|
|
573
574
|
// Store the handoffToken if it exists for desktop QR flow
|
|
574
575
|
if (sessionData.handoffToken) {
|
|
575
576
|
// Store it temporarily so it can be included in the state
|
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* ```javascript
|
|
6
6
|
* // Initialize SDK
|
|
7
7
|
* const sp = new SafePassage({
|
|
8
|
-
* apiKey: '
|
|
8
|
+
* apiKey: 'pk_xxxxx',
|
|
9
9
|
* returnUrl: 'https://merchant.com/verified',
|
|
10
10
|
* cancelUrl: 'https://merchant.com/cancelled'
|
|
11
11
|
* });
|
|
@@ -18,4 +18,4 @@
|
|
|
18
18
|
*/
|
|
19
19
|
export { SafePassage, SafePassage as default } from './core/SafePassageSDK';
|
|
20
20
|
export type { SafePassageConfig, VerificationOptions, VerificationResult, SessionValidationResponse, } from './types';
|
|
21
|
-
export declare const VERSION = "3.
|
|
21
|
+
export declare const VERSION = "3.2.1";
|
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* ```javascript
|
|
6
6
|
* // Initialize SDK
|
|
7
7
|
* const sp = new SafePassage({
|
|
8
|
-
* apiKey: '
|
|
8
|
+
* apiKey: 'pk_xxxxx',
|
|
9
9
|
* returnUrl: 'https://merchant.com/verified',
|
|
10
10
|
* cancelUrl: 'https://merchant.com/cancelled'
|
|
11
11
|
* });
|
|
@@ -25,7 +25,7 @@ if (typeof window !== 'undefined') {
|
|
|
25
25
|
}
|
|
26
26
|
export { SafePassage, SafePassage as default } from './core/SafePassageSDK';
|
|
27
27
|
// Version - Updated for handoffToken QR code fix
|
|
28
|
-
export const VERSION = '3.
|
|
28
|
+
export const VERSION = '3.2.1';
|
|
29
29
|
// For UMD builds
|
|
30
30
|
if (typeof window !== 'undefined' && window) {
|
|
31
31
|
// Dynamic import for UMD builds
|
package/dist/safepassage.min.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
/* SafePassage SDK v3.
|
|
2
|
-
"use strict";var SafePassageSDK=(()=>{var g=Object.defineProperty;var X=Object.getOwnPropertyDescriptor;var Y=Object.getOwnPropertyNames;var z=Object.prototype.hasOwnProperty;var p=(t,e)=>()=>(t&&(e=t(t=0)),e);var u=(t,e)=>{for(var n in e)g(t,n,{get:e[n],enumerable:!0})},Z=(t,e,n,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of Y(e))!z.call(t,r)&&r!==n&&g(t,r,{get:()=>e[r],enumerable:!(i=X(e,r))||i.enumerable});return t};var P=t=>Z(g({},"__esModule",{value:!0}),t);function Q(t,e){return b[e].includes(t)}function x(t,e,n=[]){let{origin:i}=t;return Q(i,e)||n.length>0&&n.some(o=>{if(o.startsWith("*.")){let s=o.slice(2);return i.endsWith(`.${s}`)||i===`https://${s}`||i===`http://${s}`}return i===o})?!0:(console.warn(`SafePassage Security: Blocked PostMessage from untrusted origin: ${i}`,{environment:e,trustedOrigins:b[e],allowedCustomOrigins:n,eventType:t.data?.type}),!1)}function T(t,e){let{data:n}=t;return!n||typeof n!="object"?{isValid:!1,error:"Invalid message format"}:n.type!=="safepassage:verification:complete"?{isValid:!1,error:"Invalid message 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}}function V(t){t==="production"&&window.location.protocol!=="https:"&&console.warn("SafePassage Warning: HTTPS recommended for production environment",{current:window.location.href})}function h(t,e){try{let n=new URL(t);if(n.protocol!=="https:"&&!(n.hostname==="localhost"||n.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 r of i)if(r.test(t))return{isValid:!1,error:"Blocked suspicious URL scheme"};return{isValid:!0}}catch{return{isValid:!1,error:"Invalid URL format"}}}function M(){if(crypto.randomUUID)return crypto.randomUUID();let t=new Uint8Array(16);crypto.getRandomValues(t);let e=Array.from(t).map(n=>n.toString(16).padStart(2,"0")).join("");return[e.slice(0,8),e.slice(8,12),"4"+e.slice(13,16),(parseInt(e.slice(16,17),16)&3|8).toString(16)+e.slice(17,20),e.slice(20,32)].join("-")}function a(t,e){console.warn(`SafePassage Security Event: ${t}`,{timestamp:new Date().toISOString(),userAgent:navigator.userAgent,url:window.location.href,...e})}var b,f,L,m=p(()=>{"use strict";b={production:["https://av.safepassageapp.com","https://portal.safepassageapp.com","https://api.safepassageapp.com"],staging:["https://av.staging.safepassageapp.com","https://portal.staging.safepassageapp.com","https://api.staging.safepassageapp.com"]};f=class{constructor(){this.attempts=new Map;this.maxAttempts=5;this.timeWindow=6e4}isAllowed(e){let n=Date.now(),r=(this.attempts.get(e)||[]).filter(o=>n-o<this.timeWindow);return r.length>=this.maxAttempts?(console.warn(`SafePassage Security: Rate limit exceeded for ${e}`),!1):(r.push(n),this.attempts.set(e,r),!0)}reset(e){this.attempts.delete(e)}},L=new f});var _={};u(_,{createSignedState:()=>te,generateHMAC:()=>w,generateSecureToken:()=>C,getSigningSecret:()=>v,parseSignedState:()=>ne,verifyHMAC:()=>k});async function w(t,e){let n=new TextEncoder,i=n.encode(e),r=n.encode(t),o=await crypto.subtle.importKey("raw",i,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),s=await crypto.subtle.sign("HMAC",o,r);return Array.from(new Uint8Array(s)).map(l=>l.toString(16).padStart(2,"0")).join("")}async function k(t,e,n){try{let i=await w(t,n);return ee(e,i)}catch{return!1}}function ee(t,e){if(t.length!==e.length)return!1;let n=0;for(let i=0;i<t.length;i++)n|=t.charCodeAt(i)^e.charCodeAt(i);return n===0}function C(t=32){let e=new Uint8Array(t);return crypto.getRandomValues(e),Array.from(e,n=>n.toString(16).padStart(2,"0")).join("")}function v(t){return{production:"safepassage-prod-hmac-2025",staging:"safepassage-stage-hmac-2025"}[t]}async function te(t,e){let n={...t,timestamp:Date.now(),nonce:C(16)},i=JSON.stringify(n),r=v(e),o=await w(i,r);return btoa(JSON.stringify({data:n,signature:o}))}async function ne(t,e,n=$){try{let i=atob(t),r=JSON.parse(i);if(!r.data||!r.signature)return console.warn("SafePassage: Invalid signed state format"),null;let{data:o,signature:s}=r,l=JSON.stringify(o),G=v(e);if(!await k(l,s,G))return console.warn("SafePassage: State signature verification failed"),null;if(o.timestamp){let E=Date.now()-o.timestamp;if(E>n)return console.warn("SafePassage: State parameter expired",{age:E,maxAge:n}),null}let{timestamp:le,nonce:pe,...J}=o;return J}catch(i){return console.warn("SafePassage: Failed to parse signed state",i),null}}var R=p(()=>{"use strict";S()});function K(t){if(!t.apiKey)throw new Error("apiKey is required");if(t.apiKey.length>N)throw new Error(`apiKey exceeds maximum length of ${N} characters`);if(!ie.test(t.apiKey))throw new Error("Invalid apiKey format. Expected pk_xxx (public) or sk_xxx (private)");if(typeof window<"u"&&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: https://docs.safepassageapp.com/server-side-sessions");if(!t.returnUrl)throw new Error("returnUrl is required");if(t.returnUrl.length>d)throw new Error(`returnUrl exceeds maximum length of ${d} characters`);if(!t.cancelUrl)throw new Error("cancelUrl is required");if(t.cancelUrl.length>d)throw new Error(`cancelUrl exceeds maximum length of ${d} characters`);let e=re(),n=h(t.returnUrl,e);if(!n.isValid)throw new Error(`returnUrl validation failed: ${n.error}`);let i=h(t.cancelUrl,e);if(!i.isValid)throw new Error(`cancelUrl validation failed: ${i.error}`);if(t.defaultChallengeAge!==void 0){if(t.defaultChallengeAge<D)throw new Error(`defaultChallengeAge must be at least ${D}`);if(t.defaultChallengeAge>O)throw new Error(`defaultChallengeAge cannot exceed ${O}`)}if(t.defaultVerificationMode&&!["L1","L2"].includes(t.defaultVerificationMode))throw new Error("defaultVerificationMode must be L1 or L2");if(t.mode&&!["redirect","new-tab"].includes(t.mode))throw new Error("mode must be redirect or new-tab")}function re(){if(typeof window>"u")return"production";let t=window.location.hostname;return t.includes("staging")||t.includes("stage")?"staging":"production"}async function W(t,e){let{createSignedState:n}=await Promise.resolve().then(()=>(R(),_));return n(t,e)}var D,O,d,N,$,ie,S=p(()=>{"use strict";m();D=25,O=150,d=2048,N=128,$=6e5,ie=/^(pk_|sk_)[a-zA-Z0-9_]+$/});function y(t){let e=H[t]||H.production;if(!e||!e.startsWith("https://"))throw new Error(`HTTPS required for ${t} environment`);return e}function oe(t){let e={production:"https://api.safepassageapp.com",staging:"https://api.staging.safepassageapp.com"},n=e[t]||e.production;if(!n||!n.startsWith("https://"))throw new Error(`HTTPS required for API URLs in ${t} environment`);return n}function q(t){let e=window.location.protocol==="https:";switch(t){case"production":e||console.warn("SafePassage Warning: HTTPS recommended for production environment");break;case"staging":e||console.warn("SafePassage Warning: HTTPS strongly recommended in staging environment");break}try{y(t),oe(t)}catch(n){let i=n instanceof Error?n.message:String(n);throw new Error(`Environment configuration validation failed: ${i}`)}}var H,j=p(()=>{"use strict";H={production:"https://av.safepassageapp.com",staging:"https://av.staging.safepassageapp.com"}});var F={};u(F,{SafePassage:()=>c,default:()=>se});var c,se,I=p(()=>{"use strict";S();j();m();c=class{constructor(e){this.popupWindow=null;this.messageListener=null;this.popupMonitorInterval=null;this.unloadListener=null;this.isVerificationInProgress=!1;this.currentSessionId=null;K(e);let n=e.environment||this.detectEnvironment();n!=="staging"&&n!=="production"&&(console.warn(`SafePassage SDK: Unknown environment '${n}', defaulting to 'production'`),n="production"),this.config={...e,environment:n,mode:e.mode||"redirect"},q(this.config.environment),V(this.config.environment),a("SDK_INITIALIZED",{environment:this.config.environment,mode:this.config.mode,origin:window.location.origin,protocol:window.location.protocol,hostname:window.location.hostname}),this.setupAutoCleanup()}async verify(e={}){let n=this.isPublicKey(),i=e.sessionId;if(n&&!i&&(i=await this.createInternalSession(e)),!n&&!i)throw new Error("sessionId is required for private API keys - must be a merchant-generated UUID v4");if(!i)throw new Error("Failed to create or obtain sessionId");if(this.isVerificationInProgress){let r=new Error(`Verification already in progress for session ${this.currentSessionId?.substring(0,8)}...`);throw a("RACE_CONDITION_PREVENTED",{currentSession:this.currentSessionId?.substring(0,8)+"...",attemptedSession:e.sessionId?e.sessionId.substring(0,8)+"...":"undefined",origin:window.location.origin}),this.config.onError?.(r),r}this.isVerificationInProgress=!0,this.currentSessionId=i;try{let r=`${this.config.apiKey}:${window.location.origin}`;if(!L.isAllowed(r)){let s=new Error("Too many verification attempts. Please wait before trying again.");throw a("RATE_LIMIT_EXCEEDED",{apiKey:this.config.apiKey.substring(0,8)+"...",origin:window.location.origin,sessionId:i?i.substring(0,8)+"...":"undefined"}),this.config.onError?.(s),s}let o=await this.buildVerificationUrl({...e,sessionId:i});a("VERIFICATION_INITIATED",{environment:this.config.environment,mode:this.config.mode,sessionId:i?i.substring(0,8)+"...":"undefined",origin:window.location.origin}),this.config.mode==="new-tab"?this.openNewTab(o,i):(this.unlockVerification(),this.redirect(o))}catch(r){throw this.unlockVerification(),r}}async buildVerificationUrl(e){let n=y(this.config.environment),i=e.challengeAge!==void 0,r=e.verificationMode!==void 0,o=i||r,s=await W({merchantId:this.config.apiKey,sessionId:e.sessionId,returnUrl:this.config.returnUrl,cancelUrl:this.config.cancelUrl,challengeAge:e.challengeAge||this.config.defaultChallengeAge,verificationMode:e.verificationMode||this.config.defaultVerificationMode,hasOverrides:o,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},handoffToken:this._temporaryHandoffToken},this.config.environment),l=new URLSearchParams({state:s,sessionId:e.sessionId,mode:this.config.mode});return`${n}/?${l.toString()}`}redirect(e){window.location.href=e}openNewTab(e,n){if(this.cleanup(),this.popupMonitorInterval&&(clearInterval(this.popupMonitorInterval),this.popupMonitorInterval=null),this.popupWindow=window.open(e,"safepassage-verify","width=600,height=700"),!this.popupWindow){this.config.onError?.(new Error("Failed to open verification window. Please check popup blocker settings."));return}this.messageListener=i=>{if(!x(i,this.config.environment)){a("POSTMESSAGE_ORIGIN_BLOCKED",{origin:i.origin,environment:this.config.environment,expectedOrigins:`SafePassage trusted origins for ${this.config.environment}`,messageType:i.data?.type});return}let r=T(i,n);if(!r.isValid){a("POSTMESSAGE_VALIDATION_FAILED",{error:r.error,origin:i.origin,sessionId:n.substring(0,8)+"...",messageType:i.data?.type});return}let o={sessionId:i.data.sessionId,status:i.data.status};a("VERIFICATION_COMPLETED",{status:o.status,sessionId:n.substring(0,8)+"...",origin:i.origin}),this.cleanup(),this.unlockVerification(),this.popupMonitorInterval&&(clearInterval(this.popupMonitorInterval),this.popupMonitorInterval=null),o.status==="verified"?this.config.onComplete?.(o):o.status==="cancelled"?this.config.onCancel?.():this.config.onError?.(new Error(`Verification failed: ${o.status}`))},window.addEventListener("message",this.messageListener),this.popupMonitorInterval=setInterval(()=>{this.popupWindow&&this.popupWindow.closed&&(a("POPUP_CLOSED_BY_USER",{sessionId:n.substring(0,8)+"...",environment:this.config.environment}),this.cleanup(),this.unlockVerification(),this.config.onCancel?.())},500)}setupAutoCleanup(){if(this.unloadListener=()=>{a("SDK_AUTO_CLEANUP",{environment:this.config.environment,trigger:"page_unload"}),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))}}detectEnvironment(){let e=window.location.hostname;return e.includes("staging")||e.includes("stage")?"staging":"production"}getEnvironment(){return this.config.environment}unlockVerification(){this.isVerificationInProgress=!1,this.currentSessionId=null,a("VERIFICATION_UNLOCKED",{environment:this.config.environment,origin:window.location.origin})}cleanup(){this.popupWindow&&!this.popupWindow.closed&&this.popupWindow.close(),this.popupWindow=null,this.messageListener&&(window.removeEventListener("message",this.messageListener),this.messageListener=null),this.popupMonitorInterval&&(clearInterval(this.popupMonitorInterval),this.popupMonitorInterval=null)}removeAutoCleanupListeners(){this.unloadListener&&(window.removeEventListener("beforeunload",this.unloadListener),window.removeEventListener("pagehide",this.unloadListener),this.unloadListener=null)}destroy(){a("SDK_DESTROYED",{environment:this.config.environment,origin:window.location.origin}),this.cleanup(),this.unlockVerification(),this.removeAutoCleanupListeners()}getPortalApiUrl(){switch(this.config.environment){case"staging":return"https://api.staging.safepassageapp.com";case"production":return"https://api.safepassageapp.com";default:return"https://api.safepassageapp.com"}}getEngineUrl(){switch(this.config.environment){case"staging":return"https://engine.staging.safepassageapp.com";case"production":return"https://engine.safepassageapp.com";default:return"https://engine.safepassageapp.com"}}getWebSocketUrl(){switch(this.config.environment){case"staging":return"wss://engine.staging.safepassageapp.com/api/websocket/stream";case"production":return"wss://engine.safepassageapp.com/api/websocket/stream";default:return"wss://engine.safepassageapp.com/api/websocket/stream"}}isPublicKey(){return this.config.apiKey.startsWith("pk_")}async createInternalSession(e){let n=M();try{let i=this.getPortalApiUrl(),r=await fetch(`${i}/api/v1/sessions/create`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.config.apiKey}`},body:JSON.stringify({merchantId:this.config.apiKey,sessionId:n,returnUrl:this.config.returnUrl,cancelUrl:this.config.cancelUrl,challengeAge:e.challengeAge,verificationMode:e.verificationMode,merchantName:document.title||window.location.hostname,externalUserId:e.externalUserId})});if(!r.ok){let s=await r.json().catch(()=>({}));throw new Error(`Failed to create session: ${r.status} ${r.statusText}. ${s.message||""}`)}let o=await r.json();return o.handoffToken&&(this._temporaryHandoffToken=o.handoffToken),a("INTERNAL_SESSION_CREATED",{sessionId:n.substring(0,8)+"...",environment:this.config.environment,apiKeyType:"public"}),n}catch(i){let r=i instanceof Error?i.message:String(i);throw a("INTERNAL_SESSION_FAILED",{error:r,environment:this.config.environment,apiKeyType:"public"}),this.config.onError?.(i),new Error(`Failed to create verification session: ${r}`)}}},se=c});var ae={};u(ae,{SafePassage:()=>c,VERSION:()=>B,default:()=>c});function U(){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 A(){let t=[];if(!window.crypto||!window.crypto.getRandomValues)throw new Error("SafePassage SDK requires Web Crypto API support");if(!window.crypto.subtle)throw new Error("SafePassage SDK requires Web Crypto subtle API for HMAC operations");crypto.randomUUID||t.push("crypto.randomUUID not supported, using polyfill"),window.URLSearchParams||t.push("URLSearchParams not supported, consider adding a polyfill for IE 11 support");try{if({a:{b:1}}?.a?.b!==1)throw new Error}catch{t.push("Optional chaining (?.) not supported, ensure transpilation for older browsers")}t.length>0&&console.warn("SafePassage SDK Browser Compatibility:",t.join("; "))}I();typeof window<"u"&&(U(),A());var B="3.0.15";if(typeof window<"u"&&window){let{SafePassage:t}=(I(),P(F)),e=window;e.SafePassage=t,e.SafePassage&&(e.SafePassage.VERSION=B)}return P(ae);})();
|
|
1
|
+
/* SafePassage SDK v3.2.1 - Redirect Implementation with Enhanced Security */
|
|
2
|
+
"use strict";var SafePassageSDK=(()=>{var u=Object.defineProperty;var J=Object.getOwnPropertyDescriptor;var X=Object.getOwnPropertyNames;var Y=Object.prototype.hasOwnProperty;var p=(t,e)=>()=>(t&&(e=t(t=0)),e);var f=(t,e)=>{for(var n in e)u(t,n,{get:e[n],enumerable:!0})},z=(t,e,n,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of X(e))!Y.call(t,r)&&r!==n&&u(t,r,{get:()=>e[r],enumerable:!(i=J(e,r))||i.enumerable});return t};var U=t=>z(u({},"__esModule",{value:!0}),t);function Z(t,e){return T[e].includes(t)}function x(t,e,n=[]){let{origin:i}=t;return Z(i,e)||n.length>0&&n.some(o=>{if(o.startsWith("*.")){let s=o.slice(2);return i.endsWith(`.${s}`)||i===`https://${s}`||i===`http://${s}`}return i===o})?!0:(console.warn(`SafePassage Security: Blocked PostMessage from untrusted origin: ${i}`,{environment:e,trustedOrigins:T[e],allowedCustomOrigins:n,eventType:t.data?.type}),!1)}function V(t,e){let{data:n}=t;return!n||typeof n!="object"?{isValid:!1,error:"Invalid message format"}:n.type!=="safepassage:verification:complete"?{isValid:!1,error:"Invalid message 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}}function M(t){t==="production"&&window.location.protocol!=="https:"&&console.warn("SafePassage Warning: HTTPS recommended for production environment",{current:window.location.href})}function m(t,e){try{let n=new URL(t);if(n.protocol!=="https:"&&!(n.hostname==="localhost"||n.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 r of i)if(r.test(t))return{isValid:!1,error:"Blocked suspicious URL scheme"};return{isValid:!0}}catch{return{isValid:!1,error:"Invalid URL format"}}}function a(t,e){console.warn(`SafePassage Security Event: ${t}`,{timestamp:new Date().toISOString(),userAgent:navigator.userAgent,url:window.location.href,...e})}var T,h,k,w=p(()=>{"use strict";T={production:["https://av.safepassageapp.com","https://portal.safepassageapp.com","https://api.safepassageapp.com"],staging:["https://av.staging.safepassageapp.com","https://portal.staging.safepassageapp.com","https://api.staging.safepassageapp.com"]};h=class{constructor(){this.attempts=new Map;this.maxAttempts=5;this.timeWindow=6e4}isAllowed(e){let n=Date.now(),r=(this.attempts.get(e)||[]).filter(o=>n-o<this.timeWindow);return r.length>=this.maxAttempts?(console.warn(`SafePassage Security: Rate limit exceeded for ${e}`),!1):(r.push(n),this.attempts.set(e,r),!0)}reset(e){this.attempts.delete(e)}},k=new h});var C={};f(C,{createSignedState:()=>ee,generateHMAC:()=>v,generateSecureToken:()=>_,getSigningSecret:()=>S,parseSignedState:()=>te,verifyHMAC:()=>L});async function v(t,e){let n=new TextEncoder,i=n.encode(e),r=n.encode(t),o=await crypto.subtle.importKey("raw",i,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),s=await crypto.subtle.sign("HMAC",o,r);return Array.from(new Uint8Array(s)).map(l=>l.toString(16).padStart(2,"0")).join("")}async function L(t,e,n){try{let i=await v(t,n);return Q(e,i)}catch{return!1}}function Q(t,e){if(t.length!==e.length)return!1;let n=0;for(let i=0;i<t.length;i++)n|=t.charCodeAt(i)^e.charCodeAt(i);return n===0}function _(t=32){let e=new Uint8Array(t);return crypto.getRandomValues(e),Array.from(e,n=>n.toString(16).padStart(2,"0")).join("")}function S(t){return{production:"safepassage-prod-hmac-2025",staging:"safepassage-stage-hmac-2025"}[t]}async function ee(t,e){let n={...t,timestamp:Date.now(),nonce:_(16)},i=JSON.stringify(n),r=S(e),o=await v(i,r);return btoa(JSON.stringify({data:n,signature:o}))}async function te(t,e,n=D){try{let i=atob(t),r=JSON.parse(i);if(!r.data||!r.signature)return console.warn("SafePassage: Invalid signed state format"),null;let{data:o,signature:s}=r,l=JSON.stringify(o),g=S(e);if(!await L(l,s,g))return console.warn("SafePassage: State signature verification failed"),null;if(o.timestamp){let P=Date.now()-o.timestamp;if(P>n)return console.warn("SafePassage: State parameter expired",{age:P,maxAge:n}),null}let{timestamp:ce,nonce:le,...G}=o;return G}catch(i){return console.warn("SafePassage: Failed to parse signed state",i),null}}var R=p(()=>{"use strict";y()});function K(t){if(!t.apiKey)throw new Error("apiKey is required");if(t.apiKey.length>N)throw new Error(`apiKey exceeds maximum length of ${N} characters`);if(!ne.test(t.apiKey))throw new Error("Invalid apiKey format. Expected pk_xxx (public) or sk_xxx (private)");if(typeof window<"u"&&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: https://docs.safepassageapp.com/server-side-sessions");if(!t.returnUrl)throw new Error("returnUrl is required");if(t.returnUrl.length>d)throw new Error(`returnUrl exceeds maximum length of ${d} characters`);if(!t.cancelUrl)throw new Error("cancelUrl is required");if(t.cancelUrl.length>d)throw new Error(`cancelUrl exceeds maximum length of ${d} characters`);let e=ie(),n=m(t.returnUrl,e);if(!n.isValid)throw new Error(`returnUrl validation failed: ${n.error}`);let i=m(t.cancelUrl,e);if(!i.isValid)throw new Error(`cancelUrl validation failed: ${i.error}`);if(t.defaultChallengeAge!==void 0){if(t.defaultChallengeAge<$)throw new Error(`defaultChallengeAge must be at least ${$}`);if(t.defaultChallengeAge>O)throw new Error(`defaultChallengeAge cannot exceed ${O}`)}if(t.defaultVerificationMode&&!["L1","L2"].includes(t.defaultVerificationMode))throw new Error("defaultVerificationMode must be L1 or L2");if(t.mode&&!["redirect","new-tab"].includes(t.mode))throw new Error("mode must be redirect or new-tab")}function ie(){if(typeof window>"u")return"production";let t=window.location.hostname;return t.includes("staging")||t.includes("stage")?"staging":"production"}async function W(t,e){let{createSignedState:n}=await Promise.resolve().then(()=>(R(),C));return n(t,e)}var $,O,d,N,D,ne,y=p(()=>{"use strict";w();$=25,O=150,d=2048,N=128,D=6e5,ne=/^(pk_|sk_)[a-zA-Z0-9_]+$/});function E(t){let e=H[t]||H.production;if(!e||!e.startsWith("https://"))throw new Error(`HTTPS required for ${t} environment`);return e}function re(t){let e={production:"https://api.safepassageapp.com",staging:"https://api.staging.safepassageapp.com"},n=e[t]||e.production;if(!n||!n.startsWith("https://"))throw new Error(`HTTPS required for API URLs in ${t} environment`);return n}function q(t){let e=window.location.protocol==="https:";switch(t){case"production":e||console.warn("SafePassage Warning: HTTPS recommended for production environment");break;case"staging":e||console.warn("SafePassage Warning: HTTPS strongly recommended in staging environment");break}try{E(t),re(t)}catch(n){let i=n instanceof Error?n.message:String(n);throw new Error(`Environment configuration validation failed: ${i}`)}}var H,j=p(()=>{"use strict";H={production:"https://av.safepassageapp.com",staging:"https://av.staging.safepassageapp.com"}});var F={};f(F,{SafePassage:()=>c,default:()=>oe});var c,oe,I=p(()=>{"use strict";y();j();w();c=class{constructor(e){this.popupWindow=null;this.messageListener=null;this.popupMonitorInterval=null;this.unloadListener=null;this.isVerificationInProgress=!1;this.currentSessionId=null;K(e);let n=e.environment||this.detectEnvironment();n!=="staging"&&n!=="production"&&(console.warn(`SafePassage SDK: Unknown environment '${n}', defaulting to 'production'`),n="production"),this.config={...e,environment:n,mode:e.mode||"redirect"},q(this.config.environment),M(this.config.environment),a("SDK_INITIALIZED",{environment:this.config.environment,mode:this.config.mode,origin:window.location.origin,protocol:window.location.protocol,hostname:window.location.hostname}),this.setupAutoCleanup()}async verify(e={}){let n=this.isPublicKey(),i;if(n)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 r=new Error(`Verification already in progress for session ${this.currentSessionId?.substring(0,8)}...`);throw a("RACE_CONDITION_PREVENTED",{currentSession:this.currentSessionId?.substring(0,8)+"...",attemptedSession:"new-session-attempt",origin:window.location.origin}),this.config.onError?.(r),r}this.isVerificationInProgress=!0,this.currentSessionId=i;try{let r=`${this.config.apiKey}:${window.location.origin}`;if(!k.isAllowed(r)){let s=new Error("Too many verification attempts. Please wait before trying again.");throw a("RATE_LIMIT_EXCEEDED",{apiKey:this.config.apiKey.substring(0,8)+"...",origin:window.location.origin,sessionId:i?i.substring(0,8)+"...":"undefined"}),this.config.onError?.(s),s}let o=await this.buildVerificationUrl(e,i);a("VERIFICATION_INITIATED",{environment:this.config.environment,mode:this.config.mode,sessionId:i?i.substring(0,8)+"...":"undefined",origin:window.location.origin}),this.config.mode==="new-tab"?this.openNewTab(o,i):this.redirect(o)}catch(r){throw this.unlockVerification(),r}}async buildVerificationUrl(e,n){let i=E(this.config.environment),r=e.challengeAge!==void 0,o=e.verificationMode!==void 0,s=r||o,l=await W({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,hasOverrides:s,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},handoffToken:this._temporaryHandoffToken},this.config.environment),g=new URLSearchParams({state:l,sessionId:n,mode:this.config.mode});return`${i}/?${g.toString()}`}redirect(e){window.location.href=e}openNewTab(e,n){if(this.cleanup(),this.popupMonitorInterval&&(clearInterval(this.popupMonitorInterval),this.popupMonitorInterval=null),this.popupWindow=window.open(e,"safepassage-verify","width=600,height=700"),!this.popupWindow){this.config.onError?.(new Error("Failed to open verification window. Please check popup blocker settings."));return}this.messageListener=i=>{if(!x(i,this.config.environment)){a("POSTMESSAGE_ORIGIN_BLOCKED",{origin:i.origin,environment:this.config.environment,expectedOrigins:`SafePassage trusted origins for ${this.config.environment}`,messageType:i.data?.type});return}let r=V(i,n);if(!r.isValid){a("POSTMESSAGE_VALIDATION_FAILED",{error:r.error,origin:i.origin,sessionId:n.substring(0,8)+"...",messageType:i.data?.type});return}let o={sessionId:i.data.sessionId,status:i.data.status};a("VERIFICATION_COMPLETED",{status:o.status,sessionId:n.substring(0,8)+"...",origin:i.origin}),this.cleanup(),this.unlockVerification(),this.popupMonitorInterval&&(clearInterval(this.popupMonitorInterval),this.popupMonitorInterval=null),o.status==="verified"?this.config.onComplete?.(o):o.status==="cancelled"?this.config.onCancel?.():this.config.onError?.(new Error(`Verification failed: ${o.status}`))},window.addEventListener("message",this.messageListener),this.popupMonitorInterval=setInterval(()=>{this.popupWindow&&this.popupWindow.closed&&(a("POPUP_CLOSED_BY_USER",{sessionId:n.substring(0,8)+"...",environment:this.config.environment}),this.cleanup(),this.unlockVerification(),this.config.onCancel?.())},500)}setupAutoCleanup(){if(this.unloadListener=()=>{a("SDK_AUTO_CLEANUP",{environment:this.config.environment,trigger:"page_unload"}),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))}}detectEnvironment(){let e=window.location.hostname;return e.includes("staging")||e.includes("stage")?"staging":"production"}getEnvironment(){return this.config.environment}unlockVerification(){this.isVerificationInProgress=!1,this.currentSessionId=null,a("VERIFICATION_UNLOCKED",{environment:this.config.environment,origin:window.location.origin})}cleanup(){this.popupWindow&&!this.popupWindow.closed&&this.popupWindow.close(),this.popupWindow=null,this.messageListener&&(window.removeEventListener("message",this.messageListener),this.messageListener=null),this.popupMonitorInterval&&(clearInterval(this.popupMonitorInterval),this.popupMonitorInterval=null)}removeAutoCleanupListeners(){this.unloadListener&&(window.removeEventListener("beforeunload",this.unloadListener),window.removeEventListener("pagehide",this.unloadListener),this.unloadListener=null)}destroy(){a("SDK_DESTROYED",{environment:this.config.environment,origin:window.location.origin}),this.cleanup(),this.unlockVerification(),this.removeAutoCleanupListeners()}getPortalApiUrl(){switch(this.config.environment){case"staging":return"https://api.staging.safepassageapp.com";case"production":return"https://api.safepassageapp.com";default:return"https://api.safepassageapp.com"}}getEngineUrl(){switch(this.config.environment){case"staging":return"https://engine.staging.safepassageapp.com";case"production":return"https://engine.safepassageapp.com";default:return"https://engine.safepassageapp.com"}}getWebSocketUrl(){switch(this.config.environment){case"staging":return"wss://engine.staging.safepassageapp.com/api/websocket/stream";case"production":return"wss://engine.safepassageapp.com/api/websocket/stream";default:return"wss://engine.safepassageapp.com/api/websocket/stream"}}isPublicKey(){return this.config.apiKey.startsWith("pk_")}async createInternalSession(e){try{let n=this.getPortalApiUrl(),i=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,challengeAge:e.challengeAge,verificationMode:e.verificationMode,merchantName:document.title||window.location.hostname,externalUserId:e.externalUserId})});if(!i.ok){let s=await i.json().catch(()=>({}));throw new Error(`Failed to create session: ${i.status} ${i.statusText}. ${s.message||""}`)}let r=await i.json(),o=r.sessionId;if(!o)throw new Error("Server did not return a sessionId");return r.handoffToken&&(this._temporaryHandoffToken=r.handoffToken),a("INTERNAL_SESSION_CREATED",{sessionId:o.substring(0,8)+"...",environment:this.config.environment,apiKeyType:"public"}),o}catch(n){let i=n instanceof Error?n.message:String(n);throw a("INTERNAL_SESSION_FAILED",{error:i,environment:this.config.environment,apiKeyType:"public"}),this.config.onError?.(n),new Error(`Failed to create verification session: ${i}`)}}},oe=c});var se={};f(se,{SafePassage:()=>c,VERSION:()=>B,default:()=>c});function A(){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 b(){let t=[];if(!window.crypto||!window.crypto.getRandomValues)throw new Error("SafePassage SDK requires Web Crypto API support");if(!window.crypto.subtle)throw new Error("SafePassage SDK requires Web Crypto subtle API for HMAC operations");crypto.randomUUID||t.push("crypto.randomUUID not supported, using polyfill"),window.URLSearchParams||t.push("URLSearchParams not supported, consider adding a polyfill for IE 11 support");try{if({a:{b:1}}?.a?.b!==1)throw new Error}catch{t.push("Optional chaining (?.) not supported, ensure transpilation for older browsers")}t.length>0&&console.warn("SafePassage SDK Browser Compatibility:",t.join("; "))}I();typeof window<"u"&&(A(),b());var B="3.2.1";if(typeof window<"u"&&window){let{SafePassage:t}=(I(),U(F)),e=window;e.SafePassage=t,e.SafePassage&&(e.SafePassage.VERSION=B)}return U(se);})();
|
|
3
3
|
if(typeof SafePassageSDK !== "undefined" && SafePassageSDK.SafePassage) { window.SafePassage = SafePassageSDK.SafePassage; window.SafePassage.VERSION = SafePassageSDK.VERSION; }
|
package/dist/types/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export interface SafePassageConfig {
|
|
5
5
|
/**
|
|
6
|
-
* Public API key (starts with
|
|
6
|
+
* Public API key (starts with pk_)
|
|
7
7
|
*/
|
|
8
8
|
apiKey: string;
|
|
9
9
|
/**
|
|
@@ -50,12 +50,6 @@ export interface SafePassageConfig {
|
|
|
50
50
|
onError?: (error: Error) => void;
|
|
51
51
|
}
|
|
52
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
53
|
/**
|
|
60
54
|
* Minimum age to verify (minimum 25)
|
|
61
55
|
* @default Uses merchant dashboard configuration
|
|
@@ -129,7 +123,6 @@ export interface SessionCreationResponse {
|
|
|
129
123
|
expiresAt: string;
|
|
130
124
|
}
|
|
131
125
|
export interface CreateSessionRequest {
|
|
132
|
-
sessionId: string;
|
|
133
126
|
returnUrl: string;
|
|
134
127
|
cancelUrl?: string;
|
|
135
128
|
challengeAge?: number;
|