@safepassage/sdk 3.4.10 → 3.4.11
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 -4
- package/core/VerificationSDK.d.ts +3 -0
- package/index.js +53 -1
- package/package.json +1 -1
- package/safepassage.min.js +2 -2
- package/sdk.min.js +2 -2
- package/types/base.d.ts +1 -0
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# SafePassage SDK v3.4.
|
|
1
|
+
# SafePassage SDK v3.4.11
|
|
2
2
|
|
|
3
3
|
A lightweight SDK for integrating SafePassage age verification into your website or application.
|
|
4
4
|
|
|
@@ -14,6 +14,9 @@ A lightweight SDK for integrating SafePassage age verification into your website
|
|
|
14
14
|
|
|
15
15
|
## Changelog
|
|
16
16
|
|
|
17
|
+
### 3.4.11
|
|
18
|
+
- Fixed CDN documentation URLs (files are at package root, not `/dist/`)
|
|
19
|
+
|
|
17
20
|
### 3.4.9
|
|
18
21
|
- Prevents `onCancel` from firing after a successful new-tab verification when the popup closes.
|
|
19
22
|
|
|
@@ -26,7 +29,7 @@ npm install @safepassage/sdk
|
|
|
26
29
|
Or load directly from jsDelivr CDN (no bundler required):
|
|
27
30
|
|
|
28
31
|
```html
|
|
29
|
-
<script src="https://cdn.jsdelivr.net/npm/@safepassage/sdk@latest/
|
|
32
|
+
<script src="https://cdn.jsdelivr.net/npm/@safepassage/sdk@latest/safepassage.min.js"></script>
|
|
30
33
|
```
|
|
31
34
|
|
|
32
35
|
## Quick Start
|
|
@@ -48,7 +51,7 @@ await sp.verify();
|
|
|
48
51
|
### With CDN (no bundler)
|
|
49
52
|
|
|
50
53
|
```html
|
|
51
|
-
<script src="https://cdn.jsdelivr.net/npm/@safepassage/sdk@latest/
|
|
54
|
+
<script src="https://cdn.jsdelivr.net/npm/@safepassage/sdk@latest/safepassage.min.js"></script>
|
|
52
55
|
<script>
|
|
53
56
|
const sp = new SafePassage({
|
|
54
57
|
apiKey: 'pk_...',
|
|
@@ -200,7 +203,7 @@ For reliable verification tracking, configure webhooks in your dashboard:
|
|
|
200
203
|
<html>
|
|
201
204
|
<head>
|
|
202
205
|
<title>Age Verification</title>
|
|
203
|
-
<script src="https://cdn.jsdelivr.net/npm/@safepassage/sdk@latest/
|
|
206
|
+
<script src="https://cdn.jsdelivr.net/npm/@safepassage/sdk@latest/safepassage.min.js"></script>
|
|
204
207
|
</head>
|
|
205
208
|
<body>
|
|
206
209
|
<button id="verify-btn">Verify Your Age</button>
|
|
@@ -47,6 +47,7 @@ export declare class VerificationSDK {
|
|
|
47
47
|
private lastVerifyUrl;
|
|
48
48
|
private lastSessionToken;
|
|
49
49
|
private lastExternalUserId;
|
|
50
|
+
private lastSandboxMode;
|
|
50
51
|
private temporaryHandoffToken;
|
|
51
52
|
private static readonly LOCAL_HOSTNAMES;
|
|
52
53
|
/**
|
|
@@ -123,6 +124,8 @@ export declare class VerificationSDK {
|
|
|
123
124
|
* Create session internally for public keys
|
|
124
125
|
*/
|
|
125
126
|
private createInternalSession;
|
|
127
|
+
private isBillingBlockError;
|
|
128
|
+
private openBillingBlockPage;
|
|
126
129
|
private getUrlConfig;
|
|
127
130
|
private getTrustedOrigins;
|
|
128
131
|
private getAllowedCustomOrigins;
|
package/index.js
CHANGED
|
@@ -469,6 +469,8 @@ var _VerificationSDK = class _VerificationSDK {
|
|
|
469
469
|
this.lastSessionToken = null;
|
|
470
470
|
// External user ID provided during verify() for cancellation redirects
|
|
471
471
|
this.lastExternalUserId = null;
|
|
472
|
+
// Sandbox mode flag from session creation (for UI labeling)
|
|
473
|
+
this.lastSandboxMode = null;
|
|
472
474
|
// Temporary storage for QR handoff token to include in state
|
|
473
475
|
this.temporaryHandoffToken = null;
|
|
474
476
|
this.brandUrls = brandUrls;
|
|
@@ -571,6 +573,7 @@ var _VerificationSDK = class _VerificationSDK {
|
|
|
571
573
|
* Build verification URL with HMAC-signed state
|
|
572
574
|
*/
|
|
573
575
|
async buildVerificationUrl(options, sessionId) {
|
|
576
|
+
var _a;
|
|
574
577
|
const baseUrl = this.config.verifyUrl || getEnvironmentUrl(this.config.environment, this.getUrlConfig());
|
|
575
578
|
const hasExplicitChallengeAge = options.challengeAge !== void 0;
|
|
576
579
|
const hasExplicitVerificationMode = options.verificationMode !== void 0;
|
|
@@ -595,7 +598,8 @@ var _VerificationSDK = class _VerificationSDK {
|
|
|
595
598
|
features: {
|
|
596
599
|
testMode: false,
|
|
597
600
|
warmupPeriodMs: 500,
|
|
598
|
-
qualityThreshold: 0.6
|
|
601
|
+
qualityThreshold: 0.6,
|
|
602
|
+
sandboxMode: (_a = this.lastSandboxMode) != null ? _a : false
|
|
599
603
|
},
|
|
600
604
|
// Include handoffToken if available (for QR code desktop flow)
|
|
601
605
|
handoffToken: this.temporaryHandoffToken || void 0,
|
|
@@ -946,6 +950,10 @@ var _VerificationSDK = class _VerificationSDK {
|
|
|
946
950
|
});
|
|
947
951
|
if (!response.ok) {
|
|
948
952
|
const errorData = await response.json().catch(() => ({}));
|
|
953
|
+
const errorCode = errorData == null ? void 0 : errorData.code;
|
|
954
|
+
if (this.isBillingBlockError(errorCode)) {
|
|
955
|
+
this.openBillingBlockPage(errorCode, errorData == null ? void 0 : errorData.portalUrl);
|
|
956
|
+
}
|
|
949
957
|
throw new Error(
|
|
950
958
|
`Failed to create session: ${response.status} ${response.statusText}. ${errorData.message || ""}`
|
|
951
959
|
);
|
|
@@ -964,6 +972,11 @@ var _VerificationSDK = class _VerificationSDK {
|
|
|
964
972
|
if (sessionData.handoffToken) {
|
|
965
973
|
this.temporaryHandoffToken = sessionData.handoffToken;
|
|
966
974
|
}
|
|
975
|
+
if (typeof sessionData.sandboxMode === "boolean") {
|
|
976
|
+
this.lastSandboxMode = sessionData.sandboxMode;
|
|
977
|
+
} else {
|
|
978
|
+
this.lastSandboxMode = null;
|
|
979
|
+
}
|
|
967
980
|
logSecurityEvent("INTERNAL_SESSION_CREATED", {
|
|
968
981
|
sessionId: sessionId.substring(0, 8) + "...",
|
|
969
982
|
environment: this.config.environment,
|
|
@@ -981,6 +994,45 @@ var _VerificationSDK = class _VerificationSDK {
|
|
|
981
994
|
throw new Error(`Failed to create verification session: ${errorMessage}`);
|
|
982
995
|
}
|
|
983
996
|
}
|
|
997
|
+
isBillingBlockError(code) {
|
|
998
|
+
return code === "SUBSCRIPTION_REQUIRED" || code === "PLAN_LIMIT_REACHED" || code === "SANDBOX_LIMIT_REACHED";
|
|
999
|
+
}
|
|
1000
|
+
openBillingBlockPage(code, portalUrl) {
|
|
1001
|
+
var _a, _b, _c, _d;
|
|
1002
|
+
try {
|
|
1003
|
+
const baseUrl = this.config.verifyUrl || getEnvironmentUrl(this.config.environment, this.getUrlConfig());
|
|
1004
|
+
const resolvedUrl = this.applyLocalVerifyOverride(baseUrl);
|
|
1005
|
+
const url = new URL(resolvedUrl);
|
|
1006
|
+
url.searchParams.set("blocked", code);
|
|
1007
|
+
if (portalUrl) {
|
|
1008
|
+
url.searchParams.set("portalUrl", portalUrl);
|
|
1009
|
+
}
|
|
1010
|
+
if (this.config.mode === "new-tab") {
|
|
1011
|
+
const target = this.config.newTabTarget || "popup";
|
|
1012
|
+
const popup = target === "tab" ? window.open(url.toString(), "_blank") : window.open(
|
|
1013
|
+
url.toString(),
|
|
1014
|
+
this.brandConstants.popupName,
|
|
1015
|
+
"width=600,height=700"
|
|
1016
|
+
);
|
|
1017
|
+
if (!popup) {
|
|
1018
|
+
(_b = (_a = this.config).onError) == null ? void 0 : _b.call(
|
|
1019
|
+
_a,
|
|
1020
|
+
new Error(
|
|
1021
|
+
"Failed to open billing notice window. Please check popup blocker settings."
|
|
1022
|
+
)
|
|
1023
|
+
);
|
|
1024
|
+
}
|
|
1025
|
+
return;
|
|
1026
|
+
}
|
|
1027
|
+
this.redirect(url.toString());
|
|
1028
|
+
} catch (error) {
|
|
1029
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1030
|
+
(_d = (_c = this.config).onError) == null ? void 0 : _d.call(
|
|
1031
|
+
_c,
|
|
1032
|
+
new Error(`Failed to open billing notice: ${errorMessage}`)
|
|
1033
|
+
);
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
984
1036
|
getUrlConfig() {
|
|
985
1037
|
return this.brandUrls[this.config.environment] || this.brandUrls.production;
|
|
986
1038
|
}
|
package/package.json
CHANGED
package/safepassage.min.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
/* SafePassage SDK v3.4.
|
|
2
|
-
"use strict";var SafePassageSDK=(()=>{var y=Object.defineProperty,de=Object.defineProperties,ge=Object.getOwnPropertyDescriptor,pe=Object.getOwnPropertyDescriptors,ue=Object.getOwnPropertyNames,v=Object.getOwnPropertySymbols;var b=Object.prototype.hasOwnProperty,H=Object.prototype.propertyIsEnumerable;var W=(n,e,t)=>e in n?y(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t,u=(n,e)=>{for(var t in e||(e={}))b.call(e,t)&&W(n,t,e[t]);if(v)for(var t of v(e))H.call(e,t)&&W(n,t,e[t]);return n},S=(n,e)=>de(n,pe(e));var B=(n,e)=>{var t={};for(var r in n)b.call(n,r)&&e.indexOf(r)<0&&(t[r]=n[r]);if(n!=null&&v)for(var r of v(n))e.indexOf(r)<0&&H.call(n,r)&&(t[r]=n[r]);return t};var T=(n,e)=>()=>(n&&(e=n(n=0)),e);var q=(n,e)=>{for(var t in e)y(n,t,{get:e[t],enumerable:!0})},fe=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of ue(e))!b.call(n,i)&&i!==t&&y(n,i,{get:()=>e[i],enumerable:!(r=ge(e,i))||r.enumerable});return n};var he=n=>fe(y({},"__esModule",{value:!0}),n);function me(n,e){return e.includes(n)}function G(n,e,t=[],r="SDK"){var s;let{origin:i}=n;return me(i,e)||t.length>0&&t.some(l=>{if(l.startsWith("*.")){let c=l.slice(2);return i.endsWith(`.${c}`)||i===`https://${c}`||i===`http://${c}`}return i===l})?!0:(console.warn(`${r} Security: Blocked PostMessage from untrusted origin: ${i}`,{trustedOrigins:e,allowedCustomOrigins:t,eventType:(s=n.data)==null?void 0:s.type}),!1)}function J(n,e,t,r){let{data:i}=n;return!i||typeof i!="object"?{isValid:!1,error:"Invalid message format"}:(r?[t,r]:[t]).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(n,e="SDK"){n==="production"&&window.location.protocol!=="https:"&&console.warn(`${e} Warning: HTTPS recommended for production environment`,{current:window.location.href})}function P(n,e,t="SDK"){try{let r=new URL(n);if(r.protocol!=="https:"&&!(r.hostname==="localhost"||r.hostname==="127.0.0.1"))return{isValid:!1,error:`HTTPS required for return URLs in ${e}`};let i=[/data:/i,/javascript:/i,/vbscript:/i,/file:/i,/ftp:/i];for(let s of i)if(s.test(n))return{isValid:!1,error:"Blocked suspicious URL scheme"};return{isValid:!0}}catch(r){return{isValid:!1,error:"Invalid URL format"}}}function d(n,e,t="SDK"){let r={timestamp:new Date().toISOString(),userAgent:typeof navigator!="undefined"?navigator.userAgent:"unknown",url:typeof window!="undefined"?window.location.href:"unknown"};console.warn(`${t} Security Event: ${n}`,u(u({},r),e))}var A,Y,x=T(()=>{"use strict";A=class{constructor(){this.attempts=new Map;this.maxAttempts=5;this.timeWindow=6e4}isAllowed(e,t="SDK"){let r=Date.now(),s=(this.attempts.get(e)||[]).filter(a=>r-a<this.timeWindow);return s.length>=this.maxAttempts?(console.warn(`${t} Security: Rate limit exceeded for ${e}`),!1):(s.push(r),this.attempts.set(e,s),!0)}reset(e){this.attempts.delete(e)}},Y=new A});var Q={};q(Q,{createSignedState:()=>ve,generateHMAC:()=>R,generateSecureToken:()=>Z,parseSignedState:()=>ye,verifyHMAC:()=>z});async function R(n,e){let t=new TextEncoder,r=t.encode(e),i=t.encode(n),s=await crypto.subtle.importKey("raw",r,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),a=await crypto.subtle.sign("HMAC",s,i);return Array.from(new Uint8Array(a)).map(l=>l.toString(16).padStart(2,"0")).join("")}async function z(n,e,t){try{let r=await R(n,t);return we(e,r)}catch(r){return!1}}function we(n,e){if(n.length!==e.length)return!1;let t=0;for(let r=0;r<n.length;r++)t|=n.charCodeAt(r)^e.charCodeAt(r);return t===0}function Z(n=32){let e=new Uint8Array(n);return crypto.getRandomValues(e),Array.from(e,t=>t.toString(16).padStart(2,"0")).join("")}async function ve(n,e){let t=S(u({},n),{timestamp:Date.now(),nonce:Z(16)}),r=JSON.stringify(t),i=await R(r,e);return btoa(JSON.stringify({data:t,signature:i}))}async function ye(n,e,t=te,r="SDK"){try{let s=atob(n),a=JSON.parse(s);if(!a.data||!a.signature)return console.warn(`${r}: Invalid signed state format`),null;let{data:l,signature:c}=a,g=JSON.stringify(l);if(!await z(g,c,e))return console.warn(`${r}: State signature verification failed`),null;if(l.timestamp){let h=Date.now()-l.timestamp;if(h>t)return console.warn(`${r}: State parameter expired`,{age:h,maxAge:t}),null}let i=l,{timestamp:p,nonce:f}=i;return B(i,["timestamp","nonce"])}catch(s){return console.warn(`${r}: Failed to parse signed state`,s),null}}var ee=T(()=>{"use strict";V()});function se(n,e){if(!n.apiKey)throw new Error("apiKey is required");if(n.apiKey.length>ie)throw new Error(`apiKey exceeds maximum length of ${ie} characters`);if(!Se.test(n.apiKey))throw new Error("Invalid apiKey format. Expected pk_xxx (public) or sk_xxx (private)");if(typeof window!="undefined"&&n.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(!n.returnUrl)throw new Error("returnUrl is required");if(n.returnUrl.length>U)throw new Error(`returnUrl exceeds maximum length of ${U} characters`);let t=Ue(),r=P(n.returnUrl,t,e.brandName);if(!r.isValid)throw new Error(`returnUrl validation failed: ${r.error}`);if(n.cancelUrl){if(n.cancelUrl.length>U)throw new Error(`cancelUrl exceeds maximum length of ${U} characters`);let i=P(n.cancelUrl,t,e.brandName);if(!i.isValid)throw new Error(`cancelUrl validation failed: ${i.error}`)}if(n.defaultChallengeAge!==void 0){if(n.defaultChallengeAge<ne)throw new Error(`defaultChallengeAge must be at least ${ne}`);if(n.defaultChallengeAge>re)throw new Error(`defaultChallengeAge cannot exceed ${re}`)}if(n.defaultVerificationMode&&!["L1","L2"].includes(n.defaultVerificationMode))throw new Error("defaultVerificationMode must be L1 or L2");if(n.mode&&!["redirect","new-tab"].includes(n.mode))throw new Error("mode must be redirect or new-tab");if(n.newTabTarget&&!["popup","tab"].includes(n.newTabTarget))throw new Error("newTabTarget must be popup or tab")}function Ue(){if(typeof window=="undefined")return"production";let n=window.location.hostname;return n.includes("staging")||n.includes("stage")?"staging":"production"}async function oe(n,e,t,r="SDK"){let{createSignedState:i}=await Promise.resolve().then(()=>(ee(),Q));return i(n,t)}var ne,re,U,ie,te,Se,V=T(()=>{"use strict";x();ne=25,re=150,U=2048,ie=128,te=6e5,Se=/^(pk_|sk_)[a-zA-Z0-9_]+$/});var Ie={};q(Ie,{SafePassage:()=>m,VERSION:()=>ce,default:()=>Ce});function F(){crypto.randomUUID||(crypto.randomUUID=function(){let n=new Uint8Array(16);crypto.getRandomValues(n),n[6]=n[6]&15|64,n[8]=n[8]&63|128;let e=Array.from(n).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 j(n="SDK"){let e=[];if(!window.crypto||!window.crypto.getRandomValues)throw new Error(`${n} requires Web Crypto API support`);if(!window.crypto.subtle)throw new Error(`${n} 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(`${n} Browser Compatibility:`,e.join("; "))}V();function L(n,e){let t=e.verifyUiUrl;if(!t||!t.startsWith("https://"))throw new Error(`HTTPS required for ${n} environment`);return t}function Ee(n,e){let t=e.apiUrl;if(!t||!t.startsWith("https://"))throw new Error(`HTTPS required for API URLs in ${n} environment`);return t}function ae(n,e,t="SDK"){let r=window.location.protocol==="https:";switch(n){case"production":r||console.warn(`${t} Warning: HTTPS recommended for production environment`);break;case"staging":r||console.warn(`${t} Warning: HTTPS strongly recommended in staging environment`);break}try{L(n,e),Ee(n,e)}catch(i){let s=i instanceof Error?i.message:String(i);throw new Error(`Environment configuration validation failed: ${s}`)}}x();var C=class C{constructor(e,t,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.temporaryHandoffToken=null;this.brandUrls=t,this.brandConstants=r,se(e,{brandName:this.brandConstants.name,docsUrl:this.brandConstants.docsUrl});let i=e.environment||this.detectEnvironment();i!=="staging"&&i!=="production"&&(console.warn(`${this.brandConstants.name} SDK: Unknown environment '${i}', defaulting to 'production'`),i="production"),this.config=S(u({},e),{environment:i,mode:e.mode||"redirect",newTabTarget:e.newTabTarget||"popup"}),ae(this.config.environment,this.getUrlConfig(),this.brandConstants.name),X(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 i,s,a,l,c,g;let t=this.isPublicKey(),r;if(t)r=await this.createInternalSession(e);else throw new Error("Private API keys (sk_) should use the direct API, not the SDK. The SDK is designed for browser-based public key usage only.");if(!r)throw new Error("Failed to obtain sessionId from server");if(this.isVerificationInProgress){let o=new Error(`Verification already in progress for session ${(i=this.currentSessionId)==null?void 0:i.substring(0,8)}...`);throw 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=(a=this.config).onError)==null||l.call(a,o),o}this.isVerificationInProgress=!0,this.currentSessionId=r,this.lastExternalUserId=e.externalUserId||null;try{let o=`${this.config.apiKey}:${window.location.origin}`;if(!Y.isAllowed(o,this.brandConstants.name)){let f=new Error("Too many verification attempts. Please wait before trying again.");throw d("RATE_LIMIT_EXCEEDED",{apiKey:this.config.apiKey.substring(0,8)+"...",origin:window.location.origin,sessionId:r?r.substring(0,8)+"...":"undefined"},this.brandConstants.name),(g=(c=this.config).onError)==null||g.call(c,f),f}let p=await this.buildVerificationUrl(e,r);d("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(p,r):(this.unlockVerification(),this.redirect(p))}catch(o){throw this.unlockVerification(),o}}async buildVerificationUrl(e,t){let r=this.config.verifyUrl||L(this.config.environment,this.getUrlConfig()),i=e.challengeAge!==void 0,s=e.verificationMode!==void 0,a=i||s,l=await oe({merchantId:this.config.apiKey,sessionId:t,returnUrl:this.config.returnUrl,cancelUrl:this.config.cancelUrl,challengeAge:e.challengeAge||this.config.defaultChallengeAge,verificationMode:e.verificationMode||this.config.defaultVerificationMode,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},handoffToken:this.temporaryHandoffToken||void 0,sessionToken:this.lastSessionToken||void 0,verifyUrl:this.lastVerifyUrl||void 0},this.config.environment,this.getHmacSecret(),this.brandConstants.name);if(this.lastVerifyUrl)try{let g=this.applyLocalVerifyOverride(this.lastVerifyUrl),o=new URL(g);return o.searchParams.set("state",l),o.searchParams.set("mode",this.config.mode),e.skipIntro&&o.searchParams.set("skip_intro","true"),e.autoReturn&&o.searchParams.set("auto_return","true"),o.toString()}catch(g){}let c=new URLSearchParams({state:l,sessionId:t,mode:this.config.mode});return e.skipIntro&&c.set("skip_intro","true"),e.autoReturn&&c.set("auto_return","true"),`${r}/?${c.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 i=this.getTrustedOrigins(),s=this.getAllowedCustomOrigins(e),a=this.brandConstants.messageType,l=this.brandConstants.legacyMessageType;this.messageListener=o=>{var k,$,O,_,M,N,K;let p=(k=o.data)==null?void 0:k.type;if(!p||typeof p!="string"||!(l?[a,l]:[a]).includes(p))return;if(!G(o,i,s,this.brandConstants.name)){d("POSTMESSAGE_ORIGIN_BLOCKED",{origin:o.origin,environment:this.config.environment,expectedOrigins:`${this.brandConstants.name} trusted origins for ${this.config.environment}`,messageType:($=o.data)==null?void 0:$.type},this.brandConstants.name);return}let I=J(o,t,a,l);if(!I.isValid){d("POSTMESSAGE_VALIDATION_FAILED",{error:I.error,origin:o.origin,sessionId:t.substring(0,8)+"...",messageType:(O=o.data)==null?void 0:O.type},this.brandConstants.name);return}let h=o.data.status;if(h==="cancelled"){this.handleCancellation(t,"postmessage");return}let w={sessionId:o.data.sessionId,status:h,timestamp:o.data.timestamp,externalUserId:o.data.externalUserId};this.hasReceivedResult=!0,d("VERIFICATION_COMPLETED",{status:w.status,sessionId:t.substring(0,8)+"...",origin:o.origin},this.brandConstants.name),this.cleanup({closePopup:!1}),this.unlockVerification(),this.popupMonitorInterval&&(clearInterval(this.popupMonitorInterval),this.popupMonitorInterval=null),w.status==="verified"?(M=(_=this.config).onComplete)==null||M.call(_,w):(K=(N=this.config).onError)==null||K.call(N,new Error(`Verification failed: ${w.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))}}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,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 r=!0;if(this.config.onCancel)try{this.config.onCancel()===!1&&(r=!1)}catch(i){d("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 t=decodeURIComponent(this.config.cancelUrl),r=new URL(t);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(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(){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 t,r;try{let i=this.getPortalApiUrl(),s=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,returnUrl:this.config.returnUrl,cancelUrl:this.config.cancelUrl,challengeAge:e.challengeAge,verificationMode:e.verificationMode,merchantName:document.title||window.location.hostname,externalUserId:e.externalUserId})});if(!s.ok){let c=await s.json().catch(()=>({}));throw new Error(`Failed to create session: ${s.status} ${s.statusText}. ${c.message||""}`)}let a=await s.json(),l=a.sessionId;if(!l)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),d("INTERNAL_SESSION_CREATED",{sessionId:l.substring(0,8)+"...",environment:this.config.environment,apiKeyType:"public"},this.brandConstants.name),l}catch(i){let s=i instanceof Error?i.message:String(i);throw d("INTERNAL_SESSION_FAILED",{error:s,environment:this.config.environment,apiKeyType:"public"},this.brandConstants.name),(r=(t=this.config).onError)==null||r.call(t,i),new Error(`Failed to create verification session: ${s}`)}}getUrlConfig(){return this.brandUrls[this.config.environment]||this.brandUrls.production}getTrustedOrigins(){return this.getUrlConfig().trustedOrigins}getAllowedCustomOrigins(e){let t=new Set,r=this.getLocalOrigin(this.config.verifyUrl||null),i=this.getLocalOrigin(e||null);return r&&t.add(r),i&&t.add(i),Array.from(t)}getLocalOrigin(e){if(!e)return null;try{let t=new URL(e);if(C.LOCAL_HOSTNAMES.has(t.hostname))return t.origin}catch(t){return null}return null}applyLocalVerifyOverride(e){let t=this.getLocalOrigin(this.config.verifyUrl||null);if(!t)return e;try{let r=new URL(t),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}};C.LOCAL_HOSTNAMES=new Set(["localhost","127.0.0.1","::1"]);var E=C;var le={production:{apiUrl:"https://api.safepassage.live",verifyUiUrl:"https://av.safepassage.live",engineUrl:"https://engine.safepassage.live",wsUrl:"wss://engine.safepassage.live/api/websocket/stream",trustedOrigins:["https://av.safepassage.live","https://portal.safepassage.live","https://api.safepassage.live"]},staging:{apiUrl:"https://api.staging.safepassage.live",verifyUiUrl:"https://av.staging.safepassage.live",engineUrl:"https://engine.staging.safepassage.live",wsUrl:"wss://engine.staging.safepassage.live/api/websocket/stream",trustedOrigins:["https://av.staging.safepassage.live","https://portal.staging.safepassage.live","https://api.staging.safepassage.live"]}},D={name:"SafePassage",hmacSecretProd:"safepassage-prod-hmac-2025",hmacSecretStaging:"safepassage-stage-hmac-2025",messageType:"safepassage:verification:complete",legacyMessageType:"safepassage-verification",popupName:"safepassage-verify",docsUrl:"https://docs.safepassage.live"};var m=class extends E{constructor(e){super(e,le,D)}},ce="3.4.9";m.VERSION=ce;typeof window!="undefined"&&(F(),j(`${D.name} SDK`));var Ce=m;return he(Ie);})();
|
|
1
|
+
/* SafePassage SDK v3.4.11 */
|
|
2
|
+
"use strict";var SafePassageSDK=(()=>{var y=Object.defineProperty,de=Object.defineProperties,ge=Object.getOwnPropertyDescriptor,pe=Object.getOwnPropertyDescriptors,ue=Object.getOwnPropertyNames,v=Object.getOwnPropertySymbols;var T=Object.prototype.hasOwnProperty,W=Object.prototype.propertyIsEnumerable;var B=(n,e,t)=>e in n?y(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t,u=(n,e)=>{for(var t in e||(e={}))T.call(e,t)&&B(n,t,e[t]);if(v)for(var t of v(e))W.call(e,t)&&B(n,t,e[t]);return n},S=(n,e)=>de(n,pe(e));var H=(n,e)=>{var t={};for(var r in n)T.call(n,r)&&e.indexOf(r)<0&&(t[r]=n[r]);if(n!=null&&v)for(var r of v(n))e.indexOf(r)<0&&W.call(n,r)&&(t[r]=n[r]);return t};var A=(n,e)=>()=>(n&&(e=n(n=0)),e);var F=(n,e)=>{for(var t in e)y(n,t,{get:e[t],enumerable:!0})},fe=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of ue(e))!T.call(n,i)&&i!==t&&y(n,i,{get:()=>e[i],enumerable:!(r=ge(e,i))||r.enumerable});return n};var he=n=>fe(y({},"__esModule",{value:!0}),n);function me(n,e){return e.includes(n)}function G(n,e,t=[],r="SDK"){var s;let{origin:i}=n;return me(i,e)||t.length>0&&t.some(a=>{if(a.startsWith("*.")){let l=a.slice(2);return i.endsWith(`.${l}`)||i===`https://${l}`||i===`http://${l}`}return i===a})?!0:(console.warn(`${r} Security: Blocked PostMessage from untrusted origin: ${i}`,{trustedOrigins:e,allowedCustomOrigins:t,eventType:(s=n.data)==null?void 0:s.type}),!1)}function J(n,e,t,r){let{data:i}=n;return!i||typeof i!="object"?{isValid:!1,error:"Invalid message format"}:(r?[t,r]:[t]).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(n,e="SDK"){n==="production"&&window.location.protocol!=="https:"&&console.warn(`${e} Warning: HTTPS recommended for production environment`,{current:window.location.href})}function x(n,e,t="SDK"){try{let r=new URL(n);if(r.protocol!=="https:"&&!(r.hostname==="localhost"||r.hostname==="127.0.0.1"))return{isValid:!1,error:`HTTPS required for return URLs in ${e}`};let i=[/data:/i,/javascript:/i,/vbscript:/i,/file:/i,/ftp:/i];for(let s of i)if(s.test(n))return{isValid:!1,error:"Blocked suspicious URL scheme"};return{isValid:!0}}catch(r){return{isValid:!1,error:"Invalid URL format"}}}function g(n,e,t="SDK"){let r={timestamp:new Date().toISOString(),userAgent:typeof navigator!="undefined"?navigator.userAgent:"unknown",url:typeof window!="undefined"?window.location.href:"unknown"};console.warn(`${t} Security Event: ${n}`,u(u({},r),e))}var P,Y,R=A(()=>{"use strict";P=class{constructor(){this.attempts=new Map;this.maxAttempts=5;this.timeWindow=6e4}isAllowed(e,t="SDK"){let r=Date.now(),s=(this.attempts.get(e)||[]).filter(o=>r-o<this.timeWindow);return s.length>=this.maxAttempts?(console.warn(`${t} Security: Rate limit exceeded for ${e}`),!1):(s.push(r),this.attempts.set(e,s),!0)}reset(e){this.attempts.delete(e)}},Y=new P});var Q={};F(Q,{createSignedState:()=>ve,generateHMAC:()=>V,generateSecureToken:()=>Z,parseSignedState:()=>ye,verifyHMAC:()=>z});async function V(n,e){let t=new TextEncoder,r=t.encode(e),i=t.encode(n),s=await crypto.subtle.importKey("raw",r,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),o=await crypto.subtle.sign("HMAC",s,i);return Array.from(new Uint8Array(o)).map(a=>a.toString(16).padStart(2,"0")).join("")}async function z(n,e,t){try{let r=await V(n,t);return we(e,r)}catch(r){return!1}}function we(n,e){if(n.length!==e.length)return!1;let t=0;for(let r=0;r<n.length;r++)t|=n.charCodeAt(r)^e.charCodeAt(r);return t===0}function Z(n=32){let e=new Uint8Array(n);return crypto.getRandomValues(e),Array.from(e,t=>t.toString(16).padStart(2,"0")).join("")}async function ve(n,e){let t=S(u({},n),{timestamp:Date.now(),nonce:Z(16)}),r=JSON.stringify(t),i=await V(r,e);return btoa(JSON.stringify({data:t,signature:i}))}async function ye(n,e,t=te,r="SDK"){try{let s=atob(n),o=JSON.parse(s);if(!o.data||!o.signature)return console.warn(`${r}: Invalid signed state format`),null;let{data:a,signature:l}=o,d=JSON.stringify(a);if(!await z(d,l,e))return console.warn(`${r}: State signature verification failed`),null;if(a.timestamp){let h=Date.now()-a.timestamp;if(h>t)return console.warn(`${r}: State parameter expired`,{age:h,maxAge:t}),null}let i=a,{timestamp:p,nonce:f}=i;return H(i,["timestamp","nonce"])}catch(s){return console.warn(`${r}: Failed to parse signed state`,s),null}}var ee=A(()=>{"use strict";L()});function se(n,e){if(!n.apiKey)throw new Error("apiKey is required");if(n.apiKey.length>ie)throw new Error(`apiKey exceeds maximum length of ${ie} characters`);if(!Se.test(n.apiKey))throw new Error("Invalid apiKey format. Expected pk_xxx (public) or sk_xxx (private)");if(typeof window!="undefined"&&n.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(!n.returnUrl)throw new Error("returnUrl is required");if(n.returnUrl.length>U)throw new Error(`returnUrl exceeds maximum length of ${U} characters`);let t=Ue(),r=x(n.returnUrl,t,e.brandName);if(!r.isValid)throw new Error(`returnUrl validation failed: ${r.error}`);if(n.cancelUrl){if(n.cancelUrl.length>U)throw new Error(`cancelUrl exceeds maximum length of ${U} characters`);let i=x(n.cancelUrl,t,e.brandName);if(!i.isValid)throw new Error(`cancelUrl validation failed: ${i.error}`)}if(n.defaultChallengeAge!==void 0){if(n.defaultChallengeAge<ne)throw new Error(`defaultChallengeAge must be at least ${ne}`);if(n.defaultChallengeAge>re)throw new Error(`defaultChallengeAge cannot exceed ${re}`)}if(n.defaultVerificationMode&&!["L1","L2"].includes(n.defaultVerificationMode))throw new Error("defaultVerificationMode must be L1 or L2");if(n.mode&&!["redirect","new-tab"].includes(n.mode))throw new Error("mode must be redirect or new-tab");if(n.newTabTarget&&!["popup","tab"].includes(n.newTabTarget))throw new Error("newTabTarget must be popup or tab")}function Ue(){if(typeof window=="undefined")return"production";let n=window.location.hostname;return n.includes("staging")||n.includes("stage")?"staging":"production"}async function oe(n,e,t,r="SDK"){let{createSignedState:i}=await Promise.resolve().then(()=>(ee(),Q));return i(n,t)}var ne,re,U,ie,te,Se,L=A(()=>{"use strict";R();ne=25,re=150,U=2048,ie=128,te=6e5,Se=/^(pk_|sk_)[a-zA-Z0-9_]+$/});var Ce={};F(Ce,{SafePassage:()=>m,VERSION:()=>ce,default:()=>be});function q(){crypto.randomUUID||(crypto.randomUUID=function(){let n=new Uint8Array(16);crypto.getRandomValues(n),n[6]=n[6]&15|64,n[8]=n[8]&63|128;let e=Array.from(n).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 j(n="SDK"){let e=[];if(!window.crypto||!window.crypto.getRandomValues)throw new Error(`${n} requires Web Crypto API support`);if(!window.crypto.subtle)throw new Error(`${n} 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(`${n} Browser Compatibility:`,e.join("; "))}L();function E(n,e){let t=e.verifyUiUrl;if(!t||!t.startsWith("https://"))throw new Error(`HTTPS required for ${n} environment`);return t}function Ee(n,e){let t=e.apiUrl;if(!t||!t.startsWith("https://"))throw new Error(`HTTPS required for API URLs in ${n} environment`);return t}function ae(n,e,t="SDK"){let r=window.location.protocol==="https:";switch(n){case"production":r||console.warn(`${t} Warning: HTTPS recommended for production environment`);break;case"staging":r||console.warn(`${t} Warning: HTTPS strongly recommended in staging environment`);break}try{E(n,e),Ee(n,e)}catch(i){let s=i instanceof Error?i.message:String(i);throw new Error(`Environment configuration validation failed: ${s}`)}}R();var C=class C{constructor(e,t,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=t,this.brandConstants=r,se(e,{brandName:this.brandConstants.name,docsUrl:this.brandConstants.docsUrl});let i=e.environment||this.detectEnvironment();i!=="staging"&&i!=="production"&&(console.warn(`${this.brandConstants.name} SDK: Unknown environment '${i}', defaulting to 'production'`),i="production"),this.config=S(u({},e),{environment:i,mode:e.mode||"redirect",newTabTarget:e.newTabTarget||"popup"}),ae(this.config.environment,this.getUrlConfig(),this.brandConstants.name),X(this.config.environment,this.brandConstants.name),g("SDK_INITIALIZED",{environment:this.config.environment,mode:this.config.mode,origin:window.location.origin,protocol:window.location.protocol,hostname:window.location.hostname},this.brandConstants.name),this.setupAutoCleanup()}async verify(e={}){var i,s,o,a,l,d;let t=this.isPublicKey(),r;if(t)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 c=new Error(`Verification already in progress for session ${(i=this.currentSessionId)==null?void 0:i.substring(0,8)}...`);throw g("RACE_CONDITION_PREVENTED",{currentSession:((s=this.currentSessionId)==null?void 0:s.substring(0,8))+"...",attemptedSession:"new-session-attempt",origin:window.location.origin},this.brandConstants.name),(a=(o=this.config).onError)==null||a.call(o,c),c}this.isVerificationInProgress=!0,this.currentSessionId=r,this.lastExternalUserId=e.externalUserId||null;try{let c=`${this.config.apiKey}:${window.location.origin}`;if(!Y.isAllowed(c,this.brandConstants.name)){let f=new Error("Too many verification attempts. Please wait before trying again.");throw g("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=(l=this.config).onError)==null||d.call(l,f),f}let p=await this.buildVerificationUrl(e,r);g("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(p,r):(this.unlockVerification(),this.redirect(p))}catch(c){throw this.unlockVerification(),c}}async buildVerificationUrl(e,t){var d;let r=this.config.verifyUrl||E(this.config.environment,this.getUrlConfig()),i=e.challengeAge!==void 0,s=e.verificationMode!==void 0,o=i||s,a=await oe({merchantId:this.config.apiKey,sessionId:t,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,sandboxMode:(d=this.lastSandboxMode)!=null?d:!1},handoffToken:this.temporaryHandoffToken||void 0,sessionToken:this.lastSessionToken||void 0,verifyUrl:this.lastVerifyUrl||void 0},this.config.environment,this.getHmacSecret(),this.brandConstants.name);if(this.lastVerifyUrl)try{let c=this.applyLocalVerifyOverride(this.lastVerifyUrl),p=new URL(c);return p.searchParams.set("state",a),p.searchParams.set("mode",this.config.mode),e.skipIntro&&p.searchParams.set("skip_intro","true"),e.autoReturn&&p.searchParams.set("auto_return","true"),p.toString()}catch(c){}let l=new URLSearchParams({state:a,sessionId:t,mode:this.config.mode});return e.skipIntro&&l.set("skip_intro","true"),e.autoReturn&&l.set("auto_return","true"),`${r}/?${l.toString()}`}redirect(e){window.location.href=e}openNewTab(e,t){var l,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=(l=this.config).onError)==null||d.call(l,new Error("Failed to open verification window. Please check popup blocker settings."));return}let i=this.getTrustedOrigins(),s=this.getAllowedCustomOrigins(e),o=this.brandConstants.messageType,a=this.brandConstants.legacyMessageType;this.messageListener=c=>{var D,M,_,O,$,N,K;let p=(D=c.data)==null?void 0:D.type;if(!p||typeof p!="string"||!(a?[o,a]:[o]).includes(p))return;if(!G(c,i,s,this.brandConstants.name)){g("POSTMESSAGE_ORIGIN_BLOCKED",{origin:c.origin,environment:this.config.environment,expectedOrigins:`${this.brandConstants.name} trusted origins for ${this.config.environment}`,messageType:(M=c.data)==null?void 0:M.type},this.brandConstants.name);return}let I=J(c,t,o,a);if(!I.isValid){g("POSTMESSAGE_VALIDATION_FAILED",{error:I.error,origin:c.origin,sessionId:t.substring(0,8)+"...",messageType:(_=c.data)==null?void 0:_.type},this.brandConstants.name);return}let h=c.data.status;if(h==="cancelled"){this.handleCancellation(t,"postmessage");return}let w={sessionId:c.data.sessionId,status:h,timestamp:c.data.timestamp,externalUserId:c.data.externalUserId};this.hasReceivedResult=!0,g("VERIFICATION_COMPLETED",{status:w.status,sessionId:t.substring(0,8)+"...",origin:c.origin},this.brandConstants.name),this.cleanup({closePopup:!1}),this.unlockVerification(),this.popupMonitorInterval&&(clearInterval(this.popupMonitorInterval),this.popupMonitorInterval=null),w.status==="verified"?($=(O=this.config).onComplete)==null||$.call(O,w):(K=(N=this.config).onError)==null||K.call(N,new Error(`Verification failed: ${w.status}`))},window.addEventListener("message",this.messageListener),this.popupMonitorInterval=setInterval(()=>{this.popupWindow&&this.popupWindow.closed&&(g("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=()=>{g("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))}}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,g("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,g("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 r=!0;if(this.config.onCancel)try{this.config.onCancel()===!1&&(r=!1)}catch(i){g("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 t=decodeURIComponent(this.config.cancelUrl),r=new URL(t);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(t){g("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(){g("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 t,r;try{let i=this.getPortalApiUrl(),s=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,returnUrl:this.config.returnUrl,cancelUrl:this.config.cancelUrl,challengeAge:e.challengeAge,verificationMode:e.verificationMode,merchantName:document.title||window.location.hostname,externalUserId:e.externalUserId})});if(!s.ok){let l=await s.json().catch(()=>({})),d=l==null?void 0:l.code;throw this.isBillingBlockError(d)&&this.openBillingBlockPage(d,l==null?void 0:l.portalUrl),new Error(`Failed to create session: ${s.status} ${s.statusText}. ${l.message||""}`)}let o=await s.json(),a=o.sessionId;if(!a)throw new Error("Server did not return a sessionId");return o.verifyUrl&&(this.lastVerifyUrl=o.verifyUrl),o.sessionToken&&(this.lastSessionToken=o.sessionToken),o.handoffToken&&(this.temporaryHandoffToken=o.handoffToken),typeof o.sandboxMode=="boolean"?this.lastSandboxMode=o.sandboxMode:this.lastSandboxMode=null,g("INTERNAL_SESSION_CREATED",{sessionId:a.substring(0,8)+"...",environment:this.config.environment,apiKeyType:"public"},this.brandConstants.name),a}catch(i){let s=i instanceof Error?i.message:String(i);throw g("INTERNAL_SESSION_FAILED",{error:s,environment:this.config.environment,apiKeyType:"public"},this.brandConstants.name),(r=(t=this.config).onError)==null||r.call(t,i),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 r,i,s,o;try{let a=this.config.verifyUrl||E(this.config.environment,this.getUrlConfig()),l=this.applyLocalVerifyOverride(a),d=new URL(l);if(d.searchParams.set("blocked",e),t&&d.searchParams.set("portalUrl",t),this.config.mode==="new-tab"){((this.config.newTabTarget||"popup")==="tab"?window.open(d.toString(),"_blank"):window.open(d.toString(),this.brandConstants.popupName,"width=600,height=700"))||(i=(r=this.config).onError)==null||i.call(r,new Error("Failed to open billing notice window. Please check popup blocker settings."));return}this.redirect(d.toString())}catch(a){let l=a instanceof Error?a.message:String(a);(o=(s=this.config).onError)==null||o.call(s,new Error(`Failed to open billing notice: ${l}`))}}getUrlConfig(){return this.brandUrls[this.config.environment]||this.brandUrls.production}getTrustedOrigins(){return this.getUrlConfig().trustedOrigins}getAllowedCustomOrigins(e){let t=new Set,r=this.getLocalOrigin(this.config.verifyUrl||null),i=this.getLocalOrigin(e||null);return r&&t.add(r),i&&t.add(i),Array.from(t)}getLocalOrigin(e){if(!e)return null;try{let t=new URL(e);if(C.LOCAL_HOSTNAMES.has(t.hostname))return t.origin}catch(t){return null}return null}applyLocalVerifyOverride(e){let t=this.getLocalOrigin(this.config.verifyUrl||null);if(!t)return e;try{let r=new URL(t),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}};C.LOCAL_HOSTNAMES=new Set(["localhost","127.0.0.1","::1"]);var b=C;var le={production:{apiUrl:"https://api.safepassage.live",verifyUiUrl:"https://av.safepassage.live",engineUrl:"https://engine.safepassage.live",wsUrl:"wss://engine.safepassage.live/api/websocket/stream",trustedOrigins:["https://av.safepassage.live","https://portal.safepassage.live","https://api.safepassage.live"]},staging:{apiUrl:"https://api.staging.safepassage.live",verifyUiUrl:"https://av.staging.safepassage.live",engineUrl:"https://engine.staging.safepassage.live",wsUrl:"wss://engine.staging.safepassage.live/api/websocket/stream",trustedOrigins:["https://av.staging.safepassage.live","https://portal.staging.safepassage.live","https://api.staging.safepassage.live"]}},k={name:"SafePassage",hmacSecretProd:"safepassage-prod-hmac-2025",hmacSecretStaging:"safepassage-stage-hmac-2025",messageType:"safepassage:verification:complete",legacyMessageType:"safepassage-verification",popupName:"safepassage-verify",docsUrl:"https://docs.safepassage.live"};var m=class extends b{constructor(e){super(e,le,k)}},ce="3.4.9";m.VERSION=ce;typeof window!="undefined"&&(q(),j(`${k.name} SDK`));var be=m;return he(Ce);})();
|
|
3
3
|
if(typeof SafePassageSDK !== "undefined" && SafePassageSDK.SafePassage) { window.SafePassage = SafePassageSDK.SafePassage; window.SafePassage.VERSION = SafePassageSDK.VERSION; }
|
package/sdk.min.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
/* SafePassage SDK v3.4.
|
|
2
|
-
"use strict";var SafePassageSDK=(()=>{var y=Object.defineProperty,de=Object.defineProperties,ge=Object.getOwnPropertyDescriptor,pe=Object.getOwnPropertyDescriptors,ue=Object.getOwnPropertyNames,v=Object.getOwnPropertySymbols;var b=Object.prototype.hasOwnProperty,H=Object.prototype.propertyIsEnumerable;var W=(n,e,t)=>e in n?y(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t,u=(n,e)=>{for(var t in e||(e={}))b.call(e,t)&&W(n,t,e[t]);if(v)for(var t of v(e))H.call(e,t)&&W(n,t,e[t]);return n},S=(n,e)=>de(n,pe(e));var B=(n,e)=>{var t={};for(var r in n)b.call(n,r)&&e.indexOf(r)<0&&(t[r]=n[r]);if(n!=null&&v)for(var r of v(n))e.indexOf(r)<0&&H.call(n,r)&&(t[r]=n[r]);return t};var T=(n,e)=>()=>(n&&(e=n(n=0)),e);var q=(n,e)=>{for(var t in e)y(n,t,{get:e[t],enumerable:!0})},fe=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of ue(e))!b.call(n,i)&&i!==t&&y(n,i,{get:()=>e[i],enumerable:!(r=ge(e,i))||r.enumerable});return n};var he=n=>fe(y({},"__esModule",{value:!0}),n);function me(n,e){return e.includes(n)}function G(n,e,t=[],r="SDK"){var s;let{origin:i}=n;return me(i,e)||t.length>0&&t.some(l=>{if(l.startsWith("*.")){let c=l.slice(2);return i.endsWith(`.${c}`)||i===`https://${c}`||i===`http://${c}`}return i===l})?!0:(console.warn(`${r} Security: Blocked PostMessage from untrusted origin: ${i}`,{trustedOrigins:e,allowedCustomOrigins:t,eventType:(s=n.data)==null?void 0:s.type}),!1)}function J(n,e,t,r){let{data:i}=n;return!i||typeof i!="object"?{isValid:!1,error:"Invalid message format"}:(r?[t,r]:[t]).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(n,e="SDK"){n==="production"&&window.location.protocol!=="https:"&&console.warn(`${e} Warning: HTTPS recommended for production environment`,{current:window.location.href})}function P(n,e,t="SDK"){try{let r=new URL(n);if(r.protocol!=="https:"&&!(r.hostname==="localhost"||r.hostname==="127.0.0.1"))return{isValid:!1,error:`HTTPS required for return URLs in ${e}`};let i=[/data:/i,/javascript:/i,/vbscript:/i,/file:/i,/ftp:/i];for(let s of i)if(s.test(n))return{isValid:!1,error:"Blocked suspicious URL scheme"};return{isValid:!0}}catch(r){return{isValid:!1,error:"Invalid URL format"}}}function d(n,e,t="SDK"){let r={timestamp:new Date().toISOString(),userAgent:typeof navigator!="undefined"?navigator.userAgent:"unknown",url:typeof window!="undefined"?window.location.href:"unknown"};console.warn(`${t} Security Event: ${n}`,u(u({},r),e))}var A,Y,x=T(()=>{"use strict";A=class{constructor(){this.attempts=new Map;this.maxAttempts=5;this.timeWindow=6e4}isAllowed(e,t="SDK"){let r=Date.now(),s=(this.attempts.get(e)||[]).filter(a=>r-a<this.timeWindow);return s.length>=this.maxAttempts?(console.warn(`${t} Security: Rate limit exceeded for ${e}`),!1):(s.push(r),this.attempts.set(e,s),!0)}reset(e){this.attempts.delete(e)}},Y=new A});var Q={};q(Q,{createSignedState:()=>ve,generateHMAC:()=>R,generateSecureToken:()=>Z,parseSignedState:()=>ye,verifyHMAC:()=>z});async function R(n,e){let t=new TextEncoder,r=t.encode(e),i=t.encode(n),s=await crypto.subtle.importKey("raw",r,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),a=await crypto.subtle.sign("HMAC",s,i);return Array.from(new Uint8Array(a)).map(l=>l.toString(16).padStart(2,"0")).join("")}async function z(n,e,t){try{let r=await R(n,t);return we(e,r)}catch(r){return!1}}function we(n,e){if(n.length!==e.length)return!1;let t=0;for(let r=0;r<n.length;r++)t|=n.charCodeAt(r)^e.charCodeAt(r);return t===0}function Z(n=32){let e=new Uint8Array(n);return crypto.getRandomValues(e),Array.from(e,t=>t.toString(16).padStart(2,"0")).join("")}async function ve(n,e){let t=S(u({},n),{timestamp:Date.now(),nonce:Z(16)}),r=JSON.stringify(t),i=await R(r,e);return btoa(JSON.stringify({data:t,signature:i}))}async function ye(n,e,t=te,r="SDK"){try{let s=atob(n),a=JSON.parse(s);if(!a.data||!a.signature)return console.warn(`${r}: Invalid signed state format`),null;let{data:l,signature:c}=a,g=JSON.stringify(l);if(!await z(g,c,e))return console.warn(`${r}: State signature verification failed`),null;if(l.timestamp){let h=Date.now()-l.timestamp;if(h>t)return console.warn(`${r}: State parameter expired`,{age:h,maxAge:t}),null}let i=l,{timestamp:p,nonce:f}=i;return B(i,["timestamp","nonce"])}catch(s){return console.warn(`${r}: Failed to parse signed state`,s),null}}var ee=T(()=>{"use strict";V()});function se(n,e){if(!n.apiKey)throw new Error("apiKey is required");if(n.apiKey.length>ie)throw new Error(`apiKey exceeds maximum length of ${ie} characters`);if(!Se.test(n.apiKey))throw new Error("Invalid apiKey format. Expected pk_xxx (public) or sk_xxx (private)");if(typeof window!="undefined"&&n.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(!n.returnUrl)throw new Error("returnUrl is required");if(n.returnUrl.length>U)throw new Error(`returnUrl exceeds maximum length of ${U} characters`);let t=Ue(),r=P(n.returnUrl,t,e.brandName);if(!r.isValid)throw new Error(`returnUrl validation failed: ${r.error}`);if(n.cancelUrl){if(n.cancelUrl.length>U)throw new Error(`cancelUrl exceeds maximum length of ${U} characters`);let i=P(n.cancelUrl,t,e.brandName);if(!i.isValid)throw new Error(`cancelUrl validation failed: ${i.error}`)}if(n.defaultChallengeAge!==void 0){if(n.defaultChallengeAge<ne)throw new Error(`defaultChallengeAge must be at least ${ne}`);if(n.defaultChallengeAge>re)throw new Error(`defaultChallengeAge cannot exceed ${re}`)}if(n.defaultVerificationMode&&!["L1","L2"].includes(n.defaultVerificationMode))throw new Error("defaultVerificationMode must be L1 or L2");if(n.mode&&!["redirect","new-tab"].includes(n.mode))throw new Error("mode must be redirect or new-tab");if(n.newTabTarget&&!["popup","tab"].includes(n.newTabTarget))throw new Error("newTabTarget must be popup or tab")}function Ue(){if(typeof window=="undefined")return"production";let n=window.location.hostname;return n.includes("staging")||n.includes("stage")?"staging":"production"}async function oe(n,e,t,r="SDK"){let{createSignedState:i}=await Promise.resolve().then(()=>(ee(),Q));return i(n,t)}var ne,re,U,ie,te,Se,V=T(()=>{"use strict";x();ne=25,re=150,U=2048,ie=128,te=6e5,Se=/^(pk_|sk_)[a-zA-Z0-9_]+$/});var Ie={};q(Ie,{SafePassage:()=>m,VERSION:()=>ce,default:()=>Ce});function F(){crypto.randomUUID||(crypto.randomUUID=function(){let n=new Uint8Array(16);crypto.getRandomValues(n),n[6]=n[6]&15|64,n[8]=n[8]&63|128;let e=Array.from(n).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 j(n="SDK"){let e=[];if(!window.crypto||!window.crypto.getRandomValues)throw new Error(`${n} requires Web Crypto API support`);if(!window.crypto.subtle)throw new Error(`${n} 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(`${n} Browser Compatibility:`,e.join("; "))}V();function L(n,e){let t=e.verifyUiUrl;if(!t||!t.startsWith("https://"))throw new Error(`HTTPS required for ${n} environment`);return t}function Ee(n,e){let t=e.apiUrl;if(!t||!t.startsWith("https://"))throw new Error(`HTTPS required for API URLs in ${n} environment`);return t}function ae(n,e,t="SDK"){let r=window.location.protocol==="https:";switch(n){case"production":r||console.warn(`${t} Warning: HTTPS recommended for production environment`);break;case"staging":r||console.warn(`${t} Warning: HTTPS strongly recommended in staging environment`);break}try{L(n,e),Ee(n,e)}catch(i){let s=i instanceof Error?i.message:String(i);throw new Error(`Environment configuration validation failed: ${s}`)}}x();var C=class C{constructor(e,t,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.temporaryHandoffToken=null;this.brandUrls=t,this.brandConstants=r,se(e,{brandName:this.brandConstants.name,docsUrl:this.brandConstants.docsUrl});let i=e.environment||this.detectEnvironment();i!=="staging"&&i!=="production"&&(console.warn(`${this.brandConstants.name} SDK: Unknown environment '${i}', defaulting to 'production'`),i="production"),this.config=S(u({},e),{environment:i,mode:e.mode||"redirect",newTabTarget:e.newTabTarget||"popup"}),ae(this.config.environment,this.getUrlConfig(),this.brandConstants.name),X(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 i,s,a,l,c,g;let t=this.isPublicKey(),r;if(t)r=await this.createInternalSession(e);else throw new Error("Private API keys (sk_) should use the direct API, not the SDK. The SDK is designed for browser-based public key usage only.");if(!r)throw new Error("Failed to obtain sessionId from server");if(this.isVerificationInProgress){let o=new Error(`Verification already in progress for session ${(i=this.currentSessionId)==null?void 0:i.substring(0,8)}...`);throw 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=(a=this.config).onError)==null||l.call(a,o),o}this.isVerificationInProgress=!0,this.currentSessionId=r,this.lastExternalUserId=e.externalUserId||null;try{let o=`${this.config.apiKey}:${window.location.origin}`;if(!Y.isAllowed(o,this.brandConstants.name)){let f=new Error("Too many verification attempts. Please wait before trying again.");throw d("RATE_LIMIT_EXCEEDED",{apiKey:this.config.apiKey.substring(0,8)+"...",origin:window.location.origin,sessionId:r?r.substring(0,8)+"...":"undefined"},this.brandConstants.name),(g=(c=this.config).onError)==null||g.call(c,f),f}let p=await this.buildVerificationUrl(e,r);d("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(p,r):(this.unlockVerification(),this.redirect(p))}catch(o){throw this.unlockVerification(),o}}async buildVerificationUrl(e,t){let r=this.config.verifyUrl||L(this.config.environment,this.getUrlConfig()),i=e.challengeAge!==void 0,s=e.verificationMode!==void 0,a=i||s,l=await oe({merchantId:this.config.apiKey,sessionId:t,returnUrl:this.config.returnUrl,cancelUrl:this.config.cancelUrl,challengeAge:e.challengeAge||this.config.defaultChallengeAge,verificationMode:e.verificationMode||this.config.defaultVerificationMode,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},handoffToken:this.temporaryHandoffToken||void 0,sessionToken:this.lastSessionToken||void 0,verifyUrl:this.lastVerifyUrl||void 0},this.config.environment,this.getHmacSecret(),this.brandConstants.name);if(this.lastVerifyUrl)try{let g=this.applyLocalVerifyOverride(this.lastVerifyUrl),o=new URL(g);return o.searchParams.set("state",l),o.searchParams.set("mode",this.config.mode),e.skipIntro&&o.searchParams.set("skip_intro","true"),e.autoReturn&&o.searchParams.set("auto_return","true"),o.toString()}catch(g){}let c=new URLSearchParams({state:l,sessionId:t,mode:this.config.mode});return e.skipIntro&&c.set("skip_intro","true"),e.autoReturn&&c.set("auto_return","true"),`${r}/?${c.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 i=this.getTrustedOrigins(),s=this.getAllowedCustomOrigins(e),a=this.brandConstants.messageType,l=this.brandConstants.legacyMessageType;this.messageListener=o=>{var k,$,O,_,M,N,K;let p=(k=o.data)==null?void 0:k.type;if(!p||typeof p!="string"||!(l?[a,l]:[a]).includes(p))return;if(!G(o,i,s,this.brandConstants.name)){d("POSTMESSAGE_ORIGIN_BLOCKED",{origin:o.origin,environment:this.config.environment,expectedOrigins:`${this.brandConstants.name} trusted origins for ${this.config.environment}`,messageType:($=o.data)==null?void 0:$.type},this.brandConstants.name);return}let I=J(o,t,a,l);if(!I.isValid){d("POSTMESSAGE_VALIDATION_FAILED",{error:I.error,origin:o.origin,sessionId:t.substring(0,8)+"...",messageType:(O=o.data)==null?void 0:O.type},this.brandConstants.name);return}let h=o.data.status;if(h==="cancelled"){this.handleCancellation(t,"postmessage");return}let w={sessionId:o.data.sessionId,status:h,timestamp:o.data.timestamp,externalUserId:o.data.externalUserId};this.hasReceivedResult=!0,d("VERIFICATION_COMPLETED",{status:w.status,sessionId:t.substring(0,8)+"...",origin:o.origin},this.brandConstants.name),this.cleanup({closePopup:!1}),this.unlockVerification(),this.popupMonitorInterval&&(clearInterval(this.popupMonitorInterval),this.popupMonitorInterval=null),w.status==="verified"?(M=(_=this.config).onComplete)==null||M.call(_,w):(K=(N=this.config).onError)==null||K.call(N,new Error(`Verification failed: ${w.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))}}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,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 r=!0;if(this.config.onCancel)try{this.config.onCancel()===!1&&(r=!1)}catch(i){d("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 t=decodeURIComponent(this.config.cancelUrl),r=new URL(t);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(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(){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 t,r;try{let i=this.getPortalApiUrl(),s=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,returnUrl:this.config.returnUrl,cancelUrl:this.config.cancelUrl,challengeAge:e.challengeAge,verificationMode:e.verificationMode,merchantName:document.title||window.location.hostname,externalUserId:e.externalUserId})});if(!s.ok){let c=await s.json().catch(()=>({}));throw new Error(`Failed to create session: ${s.status} ${s.statusText}. ${c.message||""}`)}let a=await s.json(),l=a.sessionId;if(!l)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),d("INTERNAL_SESSION_CREATED",{sessionId:l.substring(0,8)+"...",environment:this.config.environment,apiKeyType:"public"},this.brandConstants.name),l}catch(i){let s=i instanceof Error?i.message:String(i);throw d("INTERNAL_SESSION_FAILED",{error:s,environment:this.config.environment,apiKeyType:"public"},this.brandConstants.name),(r=(t=this.config).onError)==null||r.call(t,i),new Error(`Failed to create verification session: ${s}`)}}getUrlConfig(){return this.brandUrls[this.config.environment]||this.brandUrls.production}getTrustedOrigins(){return this.getUrlConfig().trustedOrigins}getAllowedCustomOrigins(e){let t=new Set,r=this.getLocalOrigin(this.config.verifyUrl||null),i=this.getLocalOrigin(e||null);return r&&t.add(r),i&&t.add(i),Array.from(t)}getLocalOrigin(e){if(!e)return null;try{let t=new URL(e);if(C.LOCAL_HOSTNAMES.has(t.hostname))return t.origin}catch(t){return null}return null}applyLocalVerifyOverride(e){let t=this.getLocalOrigin(this.config.verifyUrl||null);if(!t)return e;try{let r=new URL(t),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}};C.LOCAL_HOSTNAMES=new Set(["localhost","127.0.0.1","::1"]);var E=C;var le={production:{apiUrl:"https://api.safepassage.live",verifyUiUrl:"https://av.safepassage.live",engineUrl:"https://engine.safepassage.live",wsUrl:"wss://engine.safepassage.live/api/websocket/stream",trustedOrigins:["https://av.safepassage.live","https://portal.safepassage.live","https://api.safepassage.live"]},staging:{apiUrl:"https://api.staging.safepassage.live",verifyUiUrl:"https://av.staging.safepassage.live",engineUrl:"https://engine.staging.safepassage.live",wsUrl:"wss://engine.staging.safepassage.live/api/websocket/stream",trustedOrigins:["https://av.staging.safepassage.live","https://portal.staging.safepassage.live","https://api.staging.safepassage.live"]}},D={name:"SafePassage",hmacSecretProd:"safepassage-prod-hmac-2025",hmacSecretStaging:"safepassage-stage-hmac-2025",messageType:"safepassage:verification:complete",legacyMessageType:"safepassage-verification",popupName:"safepassage-verify",docsUrl:"https://docs.safepassage.live"};var m=class extends E{constructor(e){super(e,le,D)}},ce="3.4.9";m.VERSION=ce;typeof window!="undefined"&&(F(),j(`${D.name} SDK`));var Ce=m;return he(Ie);})();
|
|
1
|
+
/* SafePassage SDK v3.4.11 */
|
|
2
|
+
"use strict";var SafePassageSDK=(()=>{var y=Object.defineProperty,de=Object.defineProperties,ge=Object.getOwnPropertyDescriptor,pe=Object.getOwnPropertyDescriptors,ue=Object.getOwnPropertyNames,v=Object.getOwnPropertySymbols;var T=Object.prototype.hasOwnProperty,W=Object.prototype.propertyIsEnumerable;var B=(n,e,t)=>e in n?y(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t,u=(n,e)=>{for(var t in e||(e={}))T.call(e,t)&&B(n,t,e[t]);if(v)for(var t of v(e))W.call(e,t)&&B(n,t,e[t]);return n},S=(n,e)=>de(n,pe(e));var H=(n,e)=>{var t={};for(var r in n)T.call(n,r)&&e.indexOf(r)<0&&(t[r]=n[r]);if(n!=null&&v)for(var r of v(n))e.indexOf(r)<0&&W.call(n,r)&&(t[r]=n[r]);return t};var A=(n,e)=>()=>(n&&(e=n(n=0)),e);var F=(n,e)=>{for(var t in e)y(n,t,{get:e[t],enumerable:!0})},fe=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of ue(e))!T.call(n,i)&&i!==t&&y(n,i,{get:()=>e[i],enumerable:!(r=ge(e,i))||r.enumerable});return n};var he=n=>fe(y({},"__esModule",{value:!0}),n);function me(n,e){return e.includes(n)}function G(n,e,t=[],r="SDK"){var s;let{origin:i}=n;return me(i,e)||t.length>0&&t.some(a=>{if(a.startsWith("*.")){let l=a.slice(2);return i.endsWith(`.${l}`)||i===`https://${l}`||i===`http://${l}`}return i===a})?!0:(console.warn(`${r} Security: Blocked PostMessage from untrusted origin: ${i}`,{trustedOrigins:e,allowedCustomOrigins:t,eventType:(s=n.data)==null?void 0:s.type}),!1)}function J(n,e,t,r){let{data:i}=n;return!i||typeof i!="object"?{isValid:!1,error:"Invalid message format"}:(r?[t,r]:[t]).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(n,e="SDK"){n==="production"&&window.location.protocol!=="https:"&&console.warn(`${e} Warning: HTTPS recommended for production environment`,{current:window.location.href})}function x(n,e,t="SDK"){try{let r=new URL(n);if(r.protocol!=="https:"&&!(r.hostname==="localhost"||r.hostname==="127.0.0.1"))return{isValid:!1,error:`HTTPS required for return URLs in ${e}`};let i=[/data:/i,/javascript:/i,/vbscript:/i,/file:/i,/ftp:/i];for(let s of i)if(s.test(n))return{isValid:!1,error:"Blocked suspicious URL scheme"};return{isValid:!0}}catch(r){return{isValid:!1,error:"Invalid URL format"}}}function g(n,e,t="SDK"){let r={timestamp:new Date().toISOString(),userAgent:typeof navigator!="undefined"?navigator.userAgent:"unknown",url:typeof window!="undefined"?window.location.href:"unknown"};console.warn(`${t} Security Event: ${n}`,u(u({},r),e))}var P,Y,R=A(()=>{"use strict";P=class{constructor(){this.attempts=new Map;this.maxAttempts=5;this.timeWindow=6e4}isAllowed(e,t="SDK"){let r=Date.now(),s=(this.attempts.get(e)||[]).filter(o=>r-o<this.timeWindow);return s.length>=this.maxAttempts?(console.warn(`${t} Security: Rate limit exceeded for ${e}`),!1):(s.push(r),this.attempts.set(e,s),!0)}reset(e){this.attempts.delete(e)}},Y=new P});var Q={};F(Q,{createSignedState:()=>ve,generateHMAC:()=>V,generateSecureToken:()=>Z,parseSignedState:()=>ye,verifyHMAC:()=>z});async function V(n,e){let t=new TextEncoder,r=t.encode(e),i=t.encode(n),s=await crypto.subtle.importKey("raw",r,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),o=await crypto.subtle.sign("HMAC",s,i);return Array.from(new Uint8Array(o)).map(a=>a.toString(16).padStart(2,"0")).join("")}async function z(n,e,t){try{let r=await V(n,t);return we(e,r)}catch(r){return!1}}function we(n,e){if(n.length!==e.length)return!1;let t=0;for(let r=0;r<n.length;r++)t|=n.charCodeAt(r)^e.charCodeAt(r);return t===0}function Z(n=32){let e=new Uint8Array(n);return crypto.getRandomValues(e),Array.from(e,t=>t.toString(16).padStart(2,"0")).join("")}async function ve(n,e){let t=S(u({},n),{timestamp:Date.now(),nonce:Z(16)}),r=JSON.stringify(t),i=await V(r,e);return btoa(JSON.stringify({data:t,signature:i}))}async function ye(n,e,t=te,r="SDK"){try{let s=atob(n),o=JSON.parse(s);if(!o.data||!o.signature)return console.warn(`${r}: Invalid signed state format`),null;let{data:a,signature:l}=o,d=JSON.stringify(a);if(!await z(d,l,e))return console.warn(`${r}: State signature verification failed`),null;if(a.timestamp){let h=Date.now()-a.timestamp;if(h>t)return console.warn(`${r}: State parameter expired`,{age:h,maxAge:t}),null}let i=a,{timestamp:p,nonce:f}=i;return H(i,["timestamp","nonce"])}catch(s){return console.warn(`${r}: Failed to parse signed state`,s),null}}var ee=A(()=>{"use strict";L()});function se(n,e){if(!n.apiKey)throw new Error("apiKey is required");if(n.apiKey.length>ie)throw new Error(`apiKey exceeds maximum length of ${ie} characters`);if(!Se.test(n.apiKey))throw new Error("Invalid apiKey format. Expected pk_xxx (public) or sk_xxx (private)");if(typeof window!="undefined"&&n.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(!n.returnUrl)throw new Error("returnUrl is required");if(n.returnUrl.length>U)throw new Error(`returnUrl exceeds maximum length of ${U} characters`);let t=Ue(),r=x(n.returnUrl,t,e.brandName);if(!r.isValid)throw new Error(`returnUrl validation failed: ${r.error}`);if(n.cancelUrl){if(n.cancelUrl.length>U)throw new Error(`cancelUrl exceeds maximum length of ${U} characters`);let i=x(n.cancelUrl,t,e.brandName);if(!i.isValid)throw new Error(`cancelUrl validation failed: ${i.error}`)}if(n.defaultChallengeAge!==void 0){if(n.defaultChallengeAge<ne)throw new Error(`defaultChallengeAge must be at least ${ne}`);if(n.defaultChallengeAge>re)throw new Error(`defaultChallengeAge cannot exceed ${re}`)}if(n.defaultVerificationMode&&!["L1","L2"].includes(n.defaultVerificationMode))throw new Error("defaultVerificationMode must be L1 or L2");if(n.mode&&!["redirect","new-tab"].includes(n.mode))throw new Error("mode must be redirect or new-tab");if(n.newTabTarget&&!["popup","tab"].includes(n.newTabTarget))throw new Error("newTabTarget must be popup or tab")}function Ue(){if(typeof window=="undefined")return"production";let n=window.location.hostname;return n.includes("staging")||n.includes("stage")?"staging":"production"}async function oe(n,e,t,r="SDK"){let{createSignedState:i}=await Promise.resolve().then(()=>(ee(),Q));return i(n,t)}var ne,re,U,ie,te,Se,L=A(()=>{"use strict";R();ne=25,re=150,U=2048,ie=128,te=6e5,Se=/^(pk_|sk_)[a-zA-Z0-9_]+$/});var Ce={};F(Ce,{SafePassage:()=>m,VERSION:()=>ce,default:()=>be});function q(){crypto.randomUUID||(crypto.randomUUID=function(){let n=new Uint8Array(16);crypto.getRandomValues(n),n[6]=n[6]&15|64,n[8]=n[8]&63|128;let e=Array.from(n).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 j(n="SDK"){let e=[];if(!window.crypto||!window.crypto.getRandomValues)throw new Error(`${n} requires Web Crypto API support`);if(!window.crypto.subtle)throw new Error(`${n} 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(`${n} Browser Compatibility:`,e.join("; "))}L();function E(n,e){let t=e.verifyUiUrl;if(!t||!t.startsWith("https://"))throw new Error(`HTTPS required for ${n} environment`);return t}function Ee(n,e){let t=e.apiUrl;if(!t||!t.startsWith("https://"))throw new Error(`HTTPS required for API URLs in ${n} environment`);return t}function ae(n,e,t="SDK"){let r=window.location.protocol==="https:";switch(n){case"production":r||console.warn(`${t} Warning: HTTPS recommended for production environment`);break;case"staging":r||console.warn(`${t} Warning: HTTPS strongly recommended in staging environment`);break}try{E(n,e),Ee(n,e)}catch(i){let s=i instanceof Error?i.message:String(i);throw new Error(`Environment configuration validation failed: ${s}`)}}R();var C=class C{constructor(e,t,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=t,this.brandConstants=r,se(e,{brandName:this.brandConstants.name,docsUrl:this.brandConstants.docsUrl});let i=e.environment||this.detectEnvironment();i!=="staging"&&i!=="production"&&(console.warn(`${this.brandConstants.name} SDK: Unknown environment '${i}', defaulting to 'production'`),i="production"),this.config=S(u({},e),{environment:i,mode:e.mode||"redirect",newTabTarget:e.newTabTarget||"popup"}),ae(this.config.environment,this.getUrlConfig(),this.brandConstants.name),X(this.config.environment,this.brandConstants.name),g("SDK_INITIALIZED",{environment:this.config.environment,mode:this.config.mode,origin:window.location.origin,protocol:window.location.protocol,hostname:window.location.hostname},this.brandConstants.name),this.setupAutoCleanup()}async verify(e={}){var i,s,o,a,l,d;let t=this.isPublicKey(),r;if(t)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 c=new Error(`Verification already in progress for session ${(i=this.currentSessionId)==null?void 0:i.substring(0,8)}...`);throw g("RACE_CONDITION_PREVENTED",{currentSession:((s=this.currentSessionId)==null?void 0:s.substring(0,8))+"...",attemptedSession:"new-session-attempt",origin:window.location.origin},this.brandConstants.name),(a=(o=this.config).onError)==null||a.call(o,c),c}this.isVerificationInProgress=!0,this.currentSessionId=r,this.lastExternalUserId=e.externalUserId||null;try{let c=`${this.config.apiKey}:${window.location.origin}`;if(!Y.isAllowed(c,this.brandConstants.name)){let f=new Error("Too many verification attempts. Please wait before trying again.");throw g("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=(l=this.config).onError)==null||d.call(l,f),f}let p=await this.buildVerificationUrl(e,r);g("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(p,r):(this.unlockVerification(),this.redirect(p))}catch(c){throw this.unlockVerification(),c}}async buildVerificationUrl(e,t){var d;let r=this.config.verifyUrl||E(this.config.environment,this.getUrlConfig()),i=e.challengeAge!==void 0,s=e.verificationMode!==void 0,o=i||s,a=await oe({merchantId:this.config.apiKey,sessionId:t,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,sandboxMode:(d=this.lastSandboxMode)!=null?d:!1},handoffToken:this.temporaryHandoffToken||void 0,sessionToken:this.lastSessionToken||void 0,verifyUrl:this.lastVerifyUrl||void 0},this.config.environment,this.getHmacSecret(),this.brandConstants.name);if(this.lastVerifyUrl)try{let c=this.applyLocalVerifyOverride(this.lastVerifyUrl),p=new URL(c);return p.searchParams.set("state",a),p.searchParams.set("mode",this.config.mode),e.skipIntro&&p.searchParams.set("skip_intro","true"),e.autoReturn&&p.searchParams.set("auto_return","true"),p.toString()}catch(c){}let l=new URLSearchParams({state:a,sessionId:t,mode:this.config.mode});return e.skipIntro&&l.set("skip_intro","true"),e.autoReturn&&l.set("auto_return","true"),`${r}/?${l.toString()}`}redirect(e){window.location.href=e}openNewTab(e,t){var l,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=(l=this.config).onError)==null||d.call(l,new Error("Failed to open verification window. Please check popup blocker settings."));return}let i=this.getTrustedOrigins(),s=this.getAllowedCustomOrigins(e),o=this.brandConstants.messageType,a=this.brandConstants.legacyMessageType;this.messageListener=c=>{var D,M,_,O,$,N,K;let p=(D=c.data)==null?void 0:D.type;if(!p||typeof p!="string"||!(a?[o,a]:[o]).includes(p))return;if(!G(c,i,s,this.brandConstants.name)){g("POSTMESSAGE_ORIGIN_BLOCKED",{origin:c.origin,environment:this.config.environment,expectedOrigins:`${this.brandConstants.name} trusted origins for ${this.config.environment}`,messageType:(M=c.data)==null?void 0:M.type},this.brandConstants.name);return}let I=J(c,t,o,a);if(!I.isValid){g("POSTMESSAGE_VALIDATION_FAILED",{error:I.error,origin:c.origin,sessionId:t.substring(0,8)+"...",messageType:(_=c.data)==null?void 0:_.type},this.brandConstants.name);return}let h=c.data.status;if(h==="cancelled"){this.handleCancellation(t,"postmessage");return}let w={sessionId:c.data.sessionId,status:h,timestamp:c.data.timestamp,externalUserId:c.data.externalUserId};this.hasReceivedResult=!0,g("VERIFICATION_COMPLETED",{status:w.status,sessionId:t.substring(0,8)+"...",origin:c.origin},this.brandConstants.name),this.cleanup({closePopup:!1}),this.unlockVerification(),this.popupMonitorInterval&&(clearInterval(this.popupMonitorInterval),this.popupMonitorInterval=null),w.status==="verified"?($=(O=this.config).onComplete)==null||$.call(O,w):(K=(N=this.config).onError)==null||K.call(N,new Error(`Verification failed: ${w.status}`))},window.addEventListener("message",this.messageListener),this.popupMonitorInterval=setInterval(()=>{this.popupWindow&&this.popupWindow.closed&&(g("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=()=>{g("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))}}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,g("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,g("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 r=!0;if(this.config.onCancel)try{this.config.onCancel()===!1&&(r=!1)}catch(i){g("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 t=decodeURIComponent(this.config.cancelUrl),r=new URL(t);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(t){g("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(){g("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 t,r;try{let i=this.getPortalApiUrl(),s=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,returnUrl:this.config.returnUrl,cancelUrl:this.config.cancelUrl,challengeAge:e.challengeAge,verificationMode:e.verificationMode,merchantName:document.title||window.location.hostname,externalUserId:e.externalUserId})});if(!s.ok){let l=await s.json().catch(()=>({})),d=l==null?void 0:l.code;throw this.isBillingBlockError(d)&&this.openBillingBlockPage(d,l==null?void 0:l.portalUrl),new Error(`Failed to create session: ${s.status} ${s.statusText}. ${l.message||""}`)}let o=await s.json(),a=o.sessionId;if(!a)throw new Error("Server did not return a sessionId");return o.verifyUrl&&(this.lastVerifyUrl=o.verifyUrl),o.sessionToken&&(this.lastSessionToken=o.sessionToken),o.handoffToken&&(this.temporaryHandoffToken=o.handoffToken),typeof o.sandboxMode=="boolean"?this.lastSandboxMode=o.sandboxMode:this.lastSandboxMode=null,g("INTERNAL_SESSION_CREATED",{sessionId:a.substring(0,8)+"...",environment:this.config.environment,apiKeyType:"public"},this.brandConstants.name),a}catch(i){let s=i instanceof Error?i.message:String(i);throw g("INTERNAL_SESSION_FAILED",{error:s,environment:this.config.environment,apiKeyType:"public"},this.brandConstants.name),(r=(t=this.config).onError)==null||r.call(t,i),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 r,i,s,o;try{let a=this.config.verifyUrl||E(this.config.environment,this.getUrlConfig()),l=this.applyLocalVerifyOverride(a),d=new URL(l);if(d.searchParams.set("blocked",e),t&&d.searchParams.set("portalUrl",t),this.config.mode==="new-tab"){((this.config.newTabTarget||"popup")==="tab"?window.open(d.toString(),"_blank"):window.open(d.toString(),this.brandConstants.popupName,"width=600,height=700"))||(i=(r=this.config).onError)==null||i.call(r,new Error("Failed to open billing notice window. Please check popup blocker settings."));return}this.redirect(d.toString())}catch(a){let l=a instanceof Error?a.message:String(a);(o=(s=this.config).onError)==null||o.call(s,new Error(`Failed to open billing notice: ${l}`))}}getUrlConfig(){return this.brandUrls[this.config.environment]||this.brandUrls.production}getTrustedOrigins(){return this.getUrlConfig().trustedOrigins}getAllowedCustomOrigins(e){let t=new Set,r=this.getLocalOrigin(this.config.verifyUrl||null),i=this.getLocalOrigin(e||null);return r&&t.add(r),i&&t.add(i),Array.from(t)}getLocalOrigin(e){if(!e)return null;try{let t=new URL(e);if(C.LOCAL_HOSTNAMES.has(t.hostname))return t.origin}catch(t){return null}return null}applyLocalVerifyOverride(e){let t=this.getLocalOrigin(this.config.verifyUrl||null);if(!t)return e;try{let r=new URL(t),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}};C.LOCAL_HOSTNAMES=new Set(["localhost","127.0.0.1","::1"]);var b=C;var le={production:{apiUrl:"https://api.safepassage.live",verifyUiUrl:"https://av.safepassage.live",engineUrl:"https://engine.safepassage.live",wsUrl:"wss://engine.safepassage.live/api/websocket/stream",trustedOrigins:["https://av.safepassage.live","https://portal.safepassage.live","https://api.safepassage.live"]},staging:{apiUrl:"https://api.staging.safepassage.live",verifyUiUrl:"https://av.staging.safepassage.live",engineUrl:"https://engine.staging.safepassage.live",wsUrl:"wss://engine.staging.safepassage.live/api/websocket/stream",trustedOrigins:["https://av.staging.safepassage.live","https://portal.staging.safepassage.live","https://api.staging.safepassage.live"]}},k={name:"SafePassage",hmacSecretProd:"safepassage-prod-hmac-2025",hmacSecretStaging:"safepassage-stage-hmac-2025",messageType:"safepassage:verification:complete",legacyMessageType:"safepassage-verification",popupName:"safepassage-verify",docsUrl:"https://docs.safepassage.live"};var m=class extends b{constructor(e){super(e,le,k)}},ce="3.4.9";m.VERSION=ce;typeof window!="undefined"&&(q(),j(`${k.name} SDK`));var be=m;return he(Ce);})();
|
|
3
3
|
if(typeof SafePassageSDK !== "undefined" && SafePassageSDK.SafePassage) { window.SafePassage = SafePassageSDK.SafePassage; window.SafePassage.VERSION = SafePassageSDK.VERSION; }
|