@safepassage/sdk 3.4.12 → 3.5.0
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 +3 -1
- package/brands/safepassage/index.d.ts +1 -1
- package/index.js +15 -3
- package/package.json +1 -1
- package/safepassage.min.js +2 -2
- package/sdk.min.js +2 -2
- package/types/base.d.ts +12 -0
package/README.md
CHANGED
|
@@ -77,6 +77,7 @@ That's it! The SDK handles session creation automatically.
|
|
|
77
77
|
| defaultVerificationMode | string | No | `'L1'` or `'L2'` |
|
|
78
78
|
| onComplete | function | No | Callback for new-tab mode |
|
|
79
79
|
| onCancel | function | No | Called when user closes popup (new-tab mode). Return `false` to suppress automatic `cancelUrl` redirect. |
|
|
80
|
+
| language | string | No | Default UI language (`'en'`, `'de'`, `'es'`, `'fr'`). Can be overridden per verification. |
|
|
80
81
|
| onError | function | No | Error handler |
|
|
81
82
|
|
|
82
83
|
## Verification Options
|
|
@@ -89,7 +90,8 @@ await sp.verify({
|
|
|
89
90
|
verificationMode: 'L2', // Force ID verification for this session
|
|
90
91
|
externalUserId: 'user-123', // Your user ID (returned in webhooks)
|
|
91
92
|
skipIntro: true, // Skip intro screen
|
|
92
|
-
autoReturn: true
|
|
93
|
+
autoReturn: true, // Auto-redirect after success
|
|
94
|
+
language: 'es' // Override UI language for this session
|
|
93
95
|
});
|
|
94
96
|
```
|
|
95
97
|
|
|
@@ -7,6 +7,6 @@ export type SafePassageConfig = SDKConfig;
|
|
|
7
7
|
export declare class SafePassage extends VerificationSDK {
|
|
8
8
|
constructor(config: SafePassageConfig);
|
|
9
9
|
}
|
|
10
|
-
export declare const VERSION = "3.
|
|
10
|
+
export declare const VERSION = "3.5.0";
|
|
11
11
|
export type { SDKConfig, VerificationOptions, VerificationResult, SessionValidationResponse, SessionCreationResponse, CreateSessionRequest, };
|
|
12
12
|
export default SafePassage;
|
package/index.js
CHANGED
|
@@ -617,6 +617,7 @@ var _VerificationSDK = class _VerificationSDK {
|
|
|
617
617
|
this.getHmacSecret(),
|
|
618
618
|
this.brandConstants.name
|
|
619
619
|
);
|
|
620
|
+
const language = options.language || this.config.language;
|
|
620
621
|
if (this.lastVerifyUrl) {
|
|
621
622
|
try {
|
|
622
623
|
const resolvedVerifyUrl = this.applyLocalVerifyOverride(this.lastVerifyUrl);
|
|
@@ -629,6 +630,9 @@ var _VerificationSDK = class _VerificationSDK {
|
|
|
629
630
|
if (options.autoReturn) {
|
|
630
631
|
url.searchParams.set("auto_return", "true");
|
|
631
632
|
}
|
|
633
|
+
if (language) {
|
|
634
|
+
url.searchParams.set("lang", language);
|
|
635
|
+
}
|
|
632
636
|
return url.toString();
|
|
633
637
|
} catch (e) {
|
|
634
638
|
}
|
|
@@ -640,6 +644,9 @@ var _VerificationSDK = class _VerificationSDK {
|
|
|
640
644
|
if (options.autoReturn) {
|
|
641
645
|
params.set("auto_return", "true");
|
|
642
646
|
}
|
|
647
|
+
if (language) {
|
|
648
|
+
params.set("lang", language);
|
|
649
|
+
}
|
|
643
650
|
return `${baseUrl}/?${params.toString()}`;
|
|
644
651
|
}
|
|
645
652
|
/**
|
|
@@ -958,7 +965,8 @@ var _VerificationSDK = class _VerificationSDK {
|
|
|
958
965
|
const errorData = await response.json().catch(() => ({}));
|
|
959
966
|
const errorCode = errorData == null ? void 0 : errorData.code;
|
|
960
967
|
if (this.isBillingBlockError(errorCode)) {
|
|
961
|
-
|
|
968
|
+
const language = options.language || this.config.language;
|
|
969
|
+
this.openBillingBlockPage(errorCode, errorData == null ? void 0 : errorData.portalUrl, language);
|
|
962
970
|
}
|
|
963
971
|
throw new Error(
|
|
964
972
|
`Failed to create session: ${response.status} ${response.statusText}. ${errorData.message || ""}`
|
|
@@ -1003,7 +1011,7 @@ var _VerificationSDK = class _VerificationSDK {
|
|
|
1003
1011
|
isBillingBlockError(code) {
|
|
1004
1012
|
return code === "SUBSCRIPTION_REQUIRED" || code === "PLAN_LIMIT_REACHED" || code === "SANDBOX_LIMIT_REACHED";
|
|
1005
1013
|
}
|
|
1006
|
-
openBillingBlockPage(code, portalUrl) {
|
|
1014
|
+
openBillingBlockPage(code, portalUrl, language) {
|
|
1007
1015
|
var _a, _b, _c, _d;
|
|
1008
1016
|
try {
|
|
1009
1017
|
const baseUrl = this.config.verifyUrl || getEnvironmentUrl(this.config.environment, this.getUrlConfig());
|
|
@@ -1013,6 +1021,10 @@ var _VerificationSDK = class _VerificationSDK {
|
|
|
1013
1021
|
if (portalUrl) {
|
|
1014
1022
|
url.searchParams.set("portalUrl", portalUrl);
|
|
1015
1023
|
}
|
|
1024
|
+
const effectiveLanguage = language || this.config.language;
|
|
1025
|
+
if (effectiveLanguage) {
|
|
1026
|
+
url.searchParams.set("lang", effectiveLanguage);
|
|
1027
|
+
}
|
|
1016
1028
|
if (this.config.mode === "new-tab") {
|
|
1017
1029
|
const target = this.config.newTabTarget || "popup";
|
|
1018
1030
|
const popup = target === "tab" ? window.open(url.toString(), "_blank") : window.open(
|
|
@@ -1135,7 +1147,7 @@ var SafePassage = class extends VerificationSDK {
|
|
|
1135
1147
|
super(config, BRAND_URLS, BRAND_CONSTANTS);
|
|
1136
1148
|
}
|
|
1137
1149
|
};
|
|
1138
|
-
var VERSION = "3.
|
|
1150
|
+
var VERSION = "3.5.0";
|
|
1139
1151
|
SafePassage.VERSION = VERSION;
|
|
1140
1152
|
if (typeof window !== "undefined") {
|
|
1141
1153
|
setupPolyfills();
|
package/package.json
CHANGED
package/safepassage.min.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
/* SafePassage SDK v3.
|
|
2
|
-
"use strict";var SafePassageSDK=(()=>{var y=Object.defineProperty,de=Object.defineProperties,pe=Object.getOwnPropertyDescriptor,ge=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,ge(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=pe(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==="file:")return{isValid:!1,error:"file:// URLs are not supported. The verification redirect cannot return to local files. Please use a local web server (e.g., npx serve . or python3 -m http.server) instead of opening the HTML file directly."};if(r.protocol!=="https:"&&!(r.hostname==="localhost"||r.hostname==="127.0.0.1"))return{isValid:!1,error:`HTTPS required for return URLs in ${e}`};let i=[/data:/i,/javascript:/i,/vbscript:/i,/file:/i,/ftp:/i];for(let s of i)if(s.test(n))return{isValid:!1,error:"Blocked suspicious URL scheme"};return{isValid:!0}}catch(r){return{isValid:!1,error:"Invalid URL format"}}}function p(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:g,nonce:f}=i;return H(i,["timestamp","nonce"])}catch(s){return console.warn(`${r}: Failed to parse signed state`,s),null}}var ee=A(()=>{"use strict";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),p("SDK_INITIALIZED",{environment:this.config.environment,mode:this.config.mode,origin:window.location.origin,protocol:window.location.protocol,hostname:window.location.hostname},this.brandConstants.name),this.setupAutoCleanup()}async verify(e={}){var i,s,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 p("RACE_CONDITION_PREVENTED",{currentSession:((s=this.currentSessionId)==null?void 0:s.substring(0,8))+"...",attemptedSession:"new-session-attempt",origin:window.location.origin},this.brandConstants.name),(a=(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 p("RATE_LIMIT_EXCEEDED",{apiKey:this.config.apiKey.substring(0,8)+"...",origin:window.location.origin,sessionId:r?r.substring(0,8)+"...":"undefined"},this.brandConstants.name),(d=(l=this.config).onError)==null||d.call(l,f),f}let g=await this.buildVerificationUrl(e,r);p("VERIFICATION_INITIATED",{environment:this.config.environment,mode:this.config.mode,sessionId:r?r.substring(0,8)+"...":"undefined",origin:window.location.origin},this.brandConstants.name),this.config.mode==="new-tab"?this.openNewTab(g,r):(this.unlockVerification(),this.redirect(g))}catch(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),g=new URL(c);return g.searchParams.set("state",a),g.searchParams.set("mode",this.config.mode),e.skipIntro&&g.searchParams.set("skip_intro","true"),e.autoReturn&&g.searchParams.set("auto_return","true"),g.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 M,D,_,O,$,N,K;let g=(M=c.data)==null?void 0:M.type;if(!g||typeof g!="string"||!(a?[o,a]:[o]).includes(g))return;if(!G(c,i,s,this.brandConstants.name)){p("POSTMESSAGE_ORIGIN_BLOCKED",{origin:c.origin,environment:this.config.environment,expectedOrigins:`${this.brandConstants.name} trusted origins for ${this.config.environment}`,messageType:(D=c.data)==null?void 0:D.type},this.brandConstants.name);return}let I=J(c,t,o,a);if(!I.isValid){p("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,p("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&&(p("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=()=>{p("SDK_AUTO_CLEANUP",{environment:this.config.environment,trigger:"page_unload"},this.brandConstants.name),this.cleanup(),this.unlockVerification()},window.addEventListener("beforeunload",this.unloadListener),window.addEventListener("pagehide",this.unloadListener),window.history&&window.history.pushState){let e=window.history.pushState;window.history.pushState=(...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,p("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,p("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){p("CANCEL_CALLBACK_FAILED",{error:i instanceof Error?i.message:String(i),sessionId:e.substring(0,8)+"..."},this.brandConstants.name)}r&&this.redirectToCancelUrl(e)}redirectToCancelUrl(e){if(this.config.cancelUrl)try{let 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){p("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(){p("SDK_DESTROYED",{environment:this.config.environment,origin:window.location.origin},this.brandConstants.name),this.cleanup(),this.unlockVerification(),this.removeAutoCleanupListeners()}getPortalApiUrl(){return this.config.apiUrl?this.config.apiUrl:this.getUrlConfig().apiUrl}getEngineUrl(){return this.getUrlConfig().engineUrl}getWebSocketUrl(){return this.getUrlConfig().wsUrl}isPublicKey(){return this.config.apiKey.startsWith("pk_")}async createInternalSession(e){var 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,p("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 p("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.safepassageapp.com",verifyUiUrl:"https://av.safepassageapp.com",engineUrl:"https://engine.safepassageapp.com",wsUrl:"wss://engine.safepassageapp.com/api/websocket/stream",trustedOrigins:["https://av.safepassageapp.com","https://portal.safepassageapp.com","https://api.safepassageapp.com"]},staging:{apiUrl:"https://api.verityav-staging-usw1a.safepassageapp.com",verifyUiUrl:"https://av.verityav-staging-usw1a.safepassageapp.com",engineUrl:"https://engine.verityav-staging-usw1a.safepassageapp.com",wsUrl:"wss://engine.verityav-staging-usw1a.safepassageapp.com/api/websocket/stream",trustedOrigins:["https://av.verityav-staging-usw1a.safepassageapp.com","https://portal.verityav-staging-usw1a.safepassageapp.com","https://api.verityav-staging-usw1a.safepassageapp.com"]}},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.safepassageapp.com"};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);})();
|
|
1
|
+
/* SafePassage SDK v3.5.0 */
|
|
2
|
+
"use strict";var SafePassageSDK=(()=>{var S=Object.defineProperty,de=Object.defineProperties,pe=Object.getOwnPropertyDescriptor,ge=Object.getOwnPropertyDescriptors,ue=Object.getOwnPropertyNames,y=Object.getOwnPropertySymbols;var T=Object.prototype.hasOwnProperty,W=Object.prototype.propertyIsEnumerable;var B=(n,e,t)=>e in n?S(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t,f=(n,e)=>{for(var t in e||(e={}))T.call(e,t)&&B(n,t,e[t]);if(y)for(var t of y(e))W.call(e,t)&&B(n,t,e[t]);return n},U=(n,e)=>de(n,ge(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&&y)for(var r of y(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)S(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&&S(n,i,{get:()=>e[i],enumerable:!(r=pe(e,i))||r.enumerable});return n};var he=n=>fe(S({},"__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(c=>{if(c.startsWith("*.")){let l=c.slice(2);return i.endsWith(`.${l}`)||i===`https://${l}`||i===`http://${l}`}return i===c})?!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==="file:")return{isValid:!1,error:"file:// URLs are not supported. The verification redirect cannot return to local files. Please use a local web server (e.g., npx serve . or python3 -m http.server) instead of opening the HTML file directly."};if(r.protocol!=="https:"&&!(r.hostname==="localhost"||r.hostname==="127.0.0.1"))return{isValid:!1,error:`HTTPS required for return URLs in ${e}`};let i=[/data:/i,/javascript:/i,/vbscript:/i,/file:/i,/ftp:/i];for(let s of i)if(s.test(n))return{isValid:!1,error:"Blocked suspicious URL scheme"};return{isValid:!0}}catch(r){return{isValid:!1,error:"Invalid URL format"}}}function p(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}`,f(f({},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(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 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"]),a=await crypto.subtle.sign("HMAC",s,i);return Array.from(new Uint8Array(a)).map(c=>c.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=U(f({},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),a=JSON.parse(s);if(!a.data||!a.signature)return console.warn(`${r}: Invalid signed state format`),null;let{data:c,signature:l}=a,d=JSON.stringify(c);if(!await z(d,l,e))return console.warn(`${r}: State signature verification failed`),null;if(c.timestamp){let h=Date.now()-c.timestamp;if(h>t)return console.warn(`${r}: State parameter expired`,{age:h,maxAge:t}),null}let i=c,{timestamp:g,nonce:u}=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>E)throw new Error(`returnUrl exceeds maximum length of ${E} 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>E)throw new Error(`cancelUrl exceeds maximum length of ${E} 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,E,ie,te,Se,L=A(()=>{"use strict";R();ne=25,re=150,E=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 b(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{b(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 I=class I{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=U(f({},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),p("SDK_INITIALIZED",{environment:this.config.environment,mode:this.config.mode,origin:window.location.origin,protocol:window.location.protocol,hostname:window.location.hostname},this.brandConstants.name),this.setupAutoCleanup()}async verify(e={}){var i,s,a,c,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 o=new Error(`Verification already in progress for session ${(i=this.currentSessionId)==null?void 0:i.substring(0,8)}...`);throw p("RACE_CONDITION_PREVENTED",{currentSession:((s=this.currentSessionId)==null?void 0:s.substring(0,8))+"...",attemptedSession:"new-session-attempt",origin:window.location.origin},this.brandConstants.name),(c=(a=this.config).onError)==null||c.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 u=new Error("Too many verification attempts. Please wait before trying again.");throw p("RATE_LIMIT_EXCEEDED",{apiKey:this.config.apiKey.substring(0,8)+"...",origin:window.location.origin,sessionId:r?r.substring(0,8)+"...":"undefined"},this.brandConstants.name),(d=(l=this.config).onError)==null||d.call(l,u),u}let g=await this.buildVerificationUrl(e,r);p("VERIFICATION_INITIATED",{environment:this.config.environment,mode:this.config.mode,sessionId:r?r.substring(0,8)+"...":"undefined",origin:window.location.origin},this.brandConstants.name),this.config.mode==="new-tab"?this.openNewTab(g,r):(this.unlockVerification(),this.redirect(g))}catch(o){throw this.unlockVerification(),o}}async buildVerificationUrl(e,t){var o;let r=this.config.verifyUrl||b(this.config.environment,this.getUrlConfig()),i=e.challengeAge!==void 0,s=e.verificationMode!==void 0,a=i||s,c=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,sandboxMode:(o=this.lastSandboxMode)!=null?o:!1},handoffToken:this.temporaryHandoffToken||void 0,sessionToken:this.lastSessionToken||void 0,verifyUrl:this.lastVerifyUrl||void 0},this.config.environment,this.getHmacSecret(),this.brandConstants.name),l=e.language||this.config.language;if(this.lastVerifyUrl)try{let g=this.applyLocalVerifyOverride(this.lastVerifyUrl),u=new URL(g);return u.searchParams.set("state",c),u.searchParams.set("mode",this.config.mode),e.skipIntro&&u.searchParams.set("skip_intro","true"),e.autoReturn&&u.searchParams.set("auto_return","true"),l&&u.searchParams.set("lang",l),u.toString()}catch(g){}let d=new URLSearchParams({state:c,sessionId:t,mode:this.config.mode});return e.skipIntro&&d.set("skip_intro","true"),e.autoReturn&&d.set("auto_return","true"),l&&d.set("lang",l),`${r}/?${d.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),a=this.brandConstants.messageType,c=this.brandConstants.legacyMessageType;this.messageListener=o=>{var M,D,_,O,$,N,K;let g=(M=o.data)==null?void 0:M.type;if(!g||typeof g!="string"||!(c?[a,c]:[a]).includes(g))return;if(!G(o,i,s,this.brandConstants.name)){p("POSTMESSAGE_ORIGIN_BLOCKED",{origin:o.origin,environment:this.config.environment,expectedOrigins:`${this.brandConstants.name} trusted origins for ${this.config.environment}`,messageType:(D=o.data)==null?void 0:D.type},this.brandConstants.name);return}let w=J(o,t,a,c);if(!w.isValid){p("POSTMESSAGE_VALIDATION_FAILED",{error:w.error,origin:o.origin,sessionId:t.substring(0,8)+"...",messageType:(_=o.data)==null?void 0:_.type},this.brandConstants.name);return}let h=o.data.status;if(h==="cancelled"){this.handleCancellation(t,"postmessage");return}let v={sessionId:o.data.sessionId,status:h,timestamp:o.data.timestamp,externalUserId:o.data.externalUserId};this.hasReceivedResult=!0,p("VERIFICATION_COMPLETED",{status:v.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),v.status==="verified"?($=(O=this.config).onComplete)==null||$.call(O,v):(K=(N=this.config).onError)==null||K.call(N,new Error(`Verification failed: ${v.status}`))},window.addEventListener("message",this.messageListener),this.popupMonitorInterval=setInterval(()=>{this.popupWindow&&this.popupWindow.closed&&(p("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=()=>{p("SDK_AUTO_CLEANUP",{environment:this.config.environment,trigger:"page_unload"},this.brandConstants.name),this.cleanup(),this.unlockVerification()},window.addEventListener("beforeunload",this.unloadListener),window.addEventListener("pagehide",this.unloadListener),window.history&&window.history.pushState){let e=window.history.pushState;window.history.pushState=(...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,p("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,p("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){p("CANCEL_CALLBACK_FAILED",{error:i instanceof Error?i.message:String(i),sessionId:e.substring(0,8)+"..."},this.brandConstants.name)}r&&this.redirectToCancelUrl(e)}redirectToCancelUrl(e){if(this.config.cancelUrl)try{let 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){p("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(){p("SDK_DESTROYED",{environment:this.config.environment,origin:window.location.origin},this.brandConstants.name),this.cleanup(),this.unlockVerification(),this.removeAutoCleanupListeners()}getPortalApiUrl(){return this.config.apiUrl?this.config.apiUrl:this.getUrlConfig().apiUrl}getEngineUrl(){return this.getUrlConfig().engineUrl}getWebSocketUrl(){return this.getUrlConfig().wsUrl}isPublicKey(){return this.config.apiKey.startsWith("pk_")}async createInternalSession(e){var 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;if(this.isBillingBlockError(d)){let o=e.language||this.config.language;this.openBillingBlockPage(d,l==null?void 0:l.portalUrl,o)}throw new Error(`Failed to create session: ${s.status} ${s.statusText}. ${l.message||""}`)}let a=await s.json(),c=a.sessionId;if(!c)throw new Error("Server did not return a sessionId");return a.verifyUrl&&(this.lastVerifyUrl=a.verifyUrl),a.sessionToken&&(this.lastSessionToken=a.sessionToken),a.handoffToken&&(this.temporaryHandoffToken=a.handoffToken),typeof a.sandboxMode=="boolean"?this.lastSandboxMode=a.sandboxMode:this.lastSandboxMode=null,p("INTERNAL_SESSION_CREATED",{sessionId:c.substring(0,8)+"...",environment:this.config.environment,apiKeyType:"public"},this.brandConstants.name),c}catch(i){let s=i instanceof Error?i.message:String(i);throw p("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,r){var i,s,a,c;try{let l=this.config.verifyUrl||b(this.config.environment,this.getUrlConfig()),d=this.applyLocalVerifyOverride(l),o=new URL(d);o.searchParams.set("blocked",e),t&&o.searchParams.set("portalUrl",t);let g=r||this.config.language;if(g&&o.searchParams.set("lang",g),this.config.mode==="new-tab"){((this.config.newTabTarget||"popup")==="tab"?window.open(o.toString(),"_blank"):window.open(o.toString(),this.brandConstants.popupName,"width=600,height=700"))||(s=(i=this.config).onError)==null||s.call(i,new Error("Failed to open billing notice window. Please check popup blocker settings."));return}this.redirect(o.toString())}catch(l){let d=l instanceof Error?l.message:String(l);(c=(a=this.config).onError)==null||c.call(a,new Error(`Failed to open billing notice: ${d}`))}}getUrlConfig(){return this.brandUrls[this.config.environment]||this.brandUrls.production}getTrustedOrigins(){return this.getUrlConfig().trustedOrigins}getAllowedCustomOrigins(e){let 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(I.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}};I.LOCAL_HOSTNAMES=new Set(["localhost","127.0.0.1","::1"]);var C=I;var le={production:{apiUrl:"https://api.safepassageapp.com",verifyUiUrl:"https://av.safepassageapp.com",engineUrl:"https://engine.safepassageapp.com",wsUrl:"wss://engine.safepassageapp.com/api/websocket/stream",trustedOrigins:["https://av.safepassageapp.com","https://portal.safepassageapp.com","https://api.safepassageapp.com"]},staging:{apiUrl:"https://api.verityav-staging-usw1a.safepassageapp.com",verifyUiUrl:"https://av.verityav-staging-usw1a.safepassageapp.com",engineUrl:"https://engine.verityav-staging-usw1a.safepassageapp.com",wsUrl:"wss://engine.verityav-staging-usw1a.safepassageapp.com/api/websocket/stream",trustedOrigins:["https://av.verityav-staging-usw1a.safepassageapp.com","https://portal.verityav-staging-usw1a.safepassageapp.com","https://api.verityav-staging-usw1a.safepassageapp.com"]}},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.safepassageapp.com"};var m=class extends C{constructor(e){super(e,le,k)}},ce="3.5.0";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.
|
|
2
|
-
"use strict";var SafePassageSDK=(()=>{var y=Object.defineProperty,de=Object.defineProperties,pe=Object.getOwnPropertyDescriptor,ge=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,ge(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=pe(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==="file:")return{isValid:!1,error:"file:// URLs are not supported. The verification redirect cannot return to local files. Please use a local web server (e.g., npx serve . or python3 -m http.server) instead of opening the HTML file directly."};if(r.protocol!=="https:"&&!(r.hostname==="localhost"||r.hostname==="127.0.0.1"))return{isValid:!1,error:`HTTPS required for return URLs in ${e}`};let i=[/data:/i,/javascript:/i,/vbscript:/i,/file:/i,/ftp:/i];for(let s of i)if(s.test(n))return{isValid:!1,error:"Blocked suspicious URL scheme"};return{isValid:!0}}catch(r){return{isValid:!1,error:"Invalid URL format"}}}function p(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:g,nonce:f}=i;return H(i,["timestamp","nonce"])}catch(s){return console.warn(`${r}: Failed to parse signed state`,s),null}}var ee=A(()=>{"use strict";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),p("SDK_INITIALIZED",{environment:this.config.environment,mode:this.config.mode,origin:window.location.origin,protocol:window.location.protocol,hostname:window.location.hostname},this.brandConstants.name),this.setupAutoCleanup()}async verify(e={}){var i,s,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 p("RACE_CONDITION_PREVENTED",{currentSession:((s=this.currentSessionId)==null?void 0:s.substring(0,8))+"...",attemptedSession:"new-session-attempt",origin:window.location.origin},this.brandConstants.name),(a=(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 p("RATE_LIMIT_EXCEEDED",{apiKey:this.config.apiKey.substring(0,8)+"...",origin:window.location.origin,sessionId:r?r.substring(0,8)+"...":"undefined"},this.brandConstants.name),(d=(l=this.config).onError)==null||d.call(l,f),f}let g=await this.buildVerificationUrl(e,r);p("VERIFICATION_INITIATED",{environment:this.config.environment,mode:this.config.mode,sessionId:r?r.substring(0,8)+"...":"undefined",origin:window.location.origin},this.brandConstants.name),this.config.mode==="new-tab"?this.openNewTab(g,r):(this.unlockVerification(),this.redirect(g))}catch(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),g=new URL(c);return g.searchParams.set("state",a),g.searchParams.set("mode",this.config.mode),e.skipIntro&&g.searchParams.set("skip_intro","true"),e.autoReturn&&g.searchParams.set("auto_return","true"),g.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 M,D,_,O,$,N,K;let g=(M=c.data)==null?void 0:M.type;if(!g||typeof g!="string"||!(a?[o,a]:[o]).includes(g))return;if(!G(c,i,s,this.brandConstants.name)){p("POSTMESSAGE_ORIGIN_BLOCKED",{origin:c.origin,environment:this.config.environment,expectedOrigins:`${this.brandConstants.name} trusted origins for ${this.config.environment}`,messageType:(D=c.data)==null?void 0:D.type},this.brandConstants.name);return}let I=J(c,t,o,a);if(!I.isValid){p("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,p("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&&(p("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=()=>{p("SDK_AUTO_CLEANUP",{environment:this.config.environment,trigger:"page_unload"},this.brandConstants.name),this.cleanup(),this.unlockVerification()},window.addEventListener("beforeunload",this.unloadListener),window.addEventListener("pagehide",this.unloadListener),window.history&&window.history.pushState){let e=window.history.pushState;window.history.pushState=(...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,p("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,p("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){p("CANCEL_CALLBACK_FAILED",{error:i instanceof Error?i.message:String(i),sessionId:e.substring(0,8)+"..."},this.brandConstants.name)}r&&this.redirectToCancelUrl(e)}redirectToCancelUrl(e){if(this.config.cancelUrl)try{let 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){p("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(){p("SDK_DESTROYED",{environment:this.config.environment,origin:window.location.origin},this.brandConstants.name),this.cleanup(),this.unlockVerification(),this.removeAutoCleanupListeners()}getPortalApiUrl(){return this.config.apiUrl?this.config.apiUrl:this.getUrlConfig().apiUrl}getEngineUrl(){return this.getUrlConfig().engineUrl}getWebSocketUrl(){return this.getUrlConfig().wsUrl}isPublicKey(){return this.config.apiKey.startsWith("pk_")}async createInternalSession(e){var 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,p("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 p("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.safepassageapp.com",verifyUiUrl:"https://av.safepassageapp.com",engineUrl:"https://engine.safepassageapp.com",wsUrl:"wss://engine.safepassageapp.com/api/websocket/stream",trustedOrigins:["https://av.safepassageapp.com","https://portal.safepassageapp.com","https://api.safepassageapp.com"]},staging:{apiUrl:"https://api.verityav-staging-usw1a.safepassageapp.com",verifyUiUrl:"https://av.verityav-staging-usw1a.safepassageapp.com",engineUrl:"https://engine.verityav-staging-usw1a.safepassageapp.com",wsUrl:"wss://engine.verityav-staging-usw1a.safepassageapp.com/api/websocket/stream",trustedOrigins:["https://av.verityav-staging-usw1a.safepassageapp.com","https://portal.verityav-staging-usw1a.safepassageapp.com","https://api.verityav-staging-usw1a.safepassageapp.com"]}},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.safepassageapp.com"};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);})();
|
|
1
|
+
/* SafePassage SDK v3.5.0 */
|
|
2
|
+
"use strict";var SafePassageSDK=(()=>{var S=Object.defineProperty,de=Object.defineProperties,pe=Object.getOwnPropertyDescriptor,ge=Object.getOwnPropertyDescriptors,ue=Object.getOwnPropertyNames,y=Object.getOwnPropertySymbols;var T=Object.prototype.hasOwnProperty,W=Object.prototype.propertyIsEnumerable;var B=(n,e,t)=>e in n?S(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t,f=(n,e)=>{for(var t in e||(e={}))T.call(e,t)&&B(n,t,e[t]);if(y)for(var t of y(e))W.call(e,t)&&B(n,t,e[t]);return n},U=(n,e)=>de(n,ge(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&&y)for(var r of y(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)S(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&&S(n,i,{get:()=>e[i],enumerable:!(r=pe(e,i))||r.enumerable});return n};var he=n=>fe(S({},"__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(c=>{if(c.startsWith("*.")){let l=c.slice(2);return i.endsWith(`.${l}`)||i===`https://${l}`||i===`http://${l}`}return i===c})?!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==="file:")return{isValid:!1,error:"file:// URLs are not supported. The verification redirect cannot return to local files. Please use a local web server (e.g., npx serve . or python3 -m http.server) instead of opening the HTML file directly."};if(r.protocol!=="https:"&&!(r.hostname==="localhost"||r.hostname==="127.0.0.1"))return{isValid:!1,error:`HTTPS required for return URLs in ${e}`};let i=[/data:/i,/javascript:/i,/vbscript:/i,/file:/i,/ftp:/i];for(let s of i)if(s.test(n))return{isValid:!1,error:"Blocked suspicious URL scheme"};return{isValid:!0}}catch(r){return{isValid:!1,error:"Invalid URL format"}}}function p(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}`,f(f({},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(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 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"]),a=await crypto.subtle.sign("HMAC",s,i);return Array.from(new Uint8Array(a)).map(c=>c.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=U(f({},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),a=JSON.parse(s);if(!a.data||!a.signature)return console.warn(`${r}: Invalid signed state format`),null;let{data:c,signature:l}=a,d=JSON.stringify(c);if(!await z(d,l,e))return console.warn(`${r}: State signature verification failed`),null;if(c.timestamp){let h=Date.now()-c.timestamp;if(h>t)return console.warn(`${r}: State parameter expired`,{age:h,maxAge:t}),null}let i=c,{timestamp:g,nonce:u}=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>E)throw new Error(`returnUrl exceeds maximum length of ${E} 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>E)throw new Error(`cancelUrl exceeds maximum length of ${E} 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,E,ie,te,Se,L=A(()=>{"use strict";R();ne=25,re=150,E=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 b(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{b(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 I=class I{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=U(f({},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),p("SDK_INITIALIZED",{environment:this.config.environment,mode:this.config.mode,origin:window.location.origin,protocol:window.location.protocol,hostname:window.location.hostname},this.brandConstants.name),this.setupAutoCleanup()}async verify(e={}){var i,s,a,c,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 o=new Error(`Verification already in progress for session ${(i=this.currentSessionId)==null?void 0:i.substring(0,8)}...`);throw p("RACE_CONDITION_PREVENTED",{currentSession:((s=this.currentSessionId)==null?void 0:s.substring(0,8))+"...",attemptedSession:"new-session-attempt",origin:window.location.origin},this.brandConstants.name),(c=(a=this.config).onError)==null||c.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 u=new Error("Too many verification attempts. Please wait before trying again.");throw p("RATE_LIMIT_EXCEEDED",{apiKey:this.config.apiKey.substring(0,8)+"...",origin:window.location.origin,sessionId:r?r.substring(0,8)+"...":"undefined"},this.brandConstants.name),(d=(l=this.config).onError)==null||d.call(l,u),u}let g=await this.buildVerificationUrl(e,r);p("VERIFICATION_INITIATED",{environment:this.config.environment,mode:this.config.mode,sessionId:r?r.substring(0,8)+"...":"undefined",origin:window.location.origin},this.brandConstants.name),this.config.mode==="new-tab"?this.openNewTab(g,r):(this.unlockVerification(),this.redirect(g))}catch(o){throw this.unlockVerification(),o}}async buildVerificationUrl(e,t){var o;let r=this.config.verifyUrl||b(this.config.environment,this.getUrlConfig()),i=e.challengeAge!==void 0,s=e.verificationMode!==void 0,a=i||s,c=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,sandboxMode:(o=this.lastSandboxMode)!=null?o:!1},handoffToken:this.temporaryHandoffToken||void 0,sessionToken:this.lastSessionToken||void 0,verifyUrl:this.lastVerifyUrl||void 0},this.config.environment,this.getHmacSecret(),this.brandConstants.name),l=e.language||this.config.language;if(this.lastVerifyUrl)try{let g=this.applyLocalVerifyOverride(this.lastVerifyUrl),u=new URL(g);return u.searchParams.set("state",c),u.searchParams.set("mode",this.config.mode),e.skipIntro&&u.searchParams.set("skip_intro","true"),e.autoReturn&&u.searchParams.set("auto_return","true"),l&&u.searchParams.set("lang",l),u.toString()}catch(g){}let d=new URLSearchParams({state:c,sessionId:t,mode:this.config.mode});return e.skipIntro&&d.set("skip_intro","true"),e.autoReturn&&d.set("auto_return","true"),l&&d.set("lang",l),`${r}/?${d.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),a=this.brandConstants.messageType,c=this.brandConstants.legacyMessageType;this.messageListener=o=>{var M,D,_,O,$,N,K;let g=(M=o.data)==null?void 0:M.type;if(!g||typeof g!="string"||!(c?[a,c]:[a]).includes(g))return;if(!G(o,i,s,this.brandConstants.name)){p("POSTMESSAGE_ORIGIN_BLOCKED",{origin:o.origin,environment:this.config.environment,expectedOrigins:`${this.brandConstants.name} trusted origins for ${this.config.environment}`,messageType:(D=o.data)==null?void 0:D.type},this.brandConstants.name);return}let w=J(o,t,a,c);if(!w.isValid){p("POSTMESSAGE_VALIDATION_FAILED",{error:w.error,origin:o.origin,sessionId:t.substring(0,8)+"...",messageType:(_=o.data)==null?void 0:_.type},this.brandConstants.name);return}let h=o.data.status;if(h==="cancelled"){this.handleCancellation(t,"postmessage");return}let v={sessionId:o.data.sessionId,status:h,timestamp:o.data.timestamp,externalUserId:o.data.externalUserId};this.hasReceivedResult=!0,p("VERIFICATION_COMPLETED",{status:v.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),v.status==="verified"?($=(O=this.config).onComplete)==null||$.call(O,v):(K=(N=this.config).onError)==null||K.call(N,new Error(`Verification failed: ${v.status}`))},window.addEventListener("message",this.messageListener),this.popupMonitorInterval=setInterval(()=>{this.popupWindow&&this.popupWindow.closed&&(p("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=()=>{p("SDK_AUTO_CLEANUP",{environment:this.config.environment,trigger:"page_unload"},this.brandConstants.name),this.cleanup(),this.unlockVerification()},window.addEventListener("beforeunload",this.unloadListener),window.addEventListener("pagehide",this.unloadListener),window.history&&window.history.pushState){let e=window.history.pushState;window.history.pushState=(...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,p("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,p("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){p("CANCEL_CALLBACK_FAILED",{error:i instanceof Error?i.message:String(i),sessionId:e.substring(0,8)+"..."},this.brandConstants.name)}r&&this.redirectToCancelUrl(e)}redirectToCancelUrl(e){if(this.config.cancelUrl)try{let 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){p("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(){p("SDK_DESTROYED",{environment:this.config.environment,origin:window.location.origin},this.brandConstants.name),this.cleanup(),this.unlockVerification(),this.removeAutoCleanupListeners()}getPortalApiUrl(){return this.config.apiUrl?this.config.apiUrl:this.getUrlConfig().apiUrl}getEngineUrl(){return this.getUrlConfig().engineUrl}getWebSocketUrl(){return this.getUrlConfig().wsUrl}isPublicKey(){return this.config.apiKey.startsWith("pk_")}async createInternalSession(e){var 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;if(this.isBillingBlockError(d)){let o=e.language||this.config.language;this.openBillingBlockPage(d,l==null?void 0:l.portalUrl,o)}throw new Error(`Failed to create session: ${s.status} ${s.statusText}. ${l.message||""}`)}let a=await s.json(),c=a.sessionId;if(!c)throw new Error("Server did not return a sessionId");return a.verifyUrl&&(this.lastVerifyUrl=a.verifyUrl),a.sessionToken&&(this.lastSessionToken=a.sessionToken),a.handoffToken&&(this.temporaryHandoffToken=a.handoffToken),typeof a.sandboxMode=="boolean"?this.lastSandboxMode=a.sandboxMode:this.lastSandboxMode=null,p("INTERNAL_SESSION_CREATED",{sessionId:c.substring(0,8)+"...",environment:this.config.environment,apiKeyType:"public"},this.brandConstants.name),c}catch(i){let s=i instanceof Error?i.message:String(i);throw p("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,r){var i,s,a,c;try{let l=this.config.verifyUrl||b(this.config.environment,this.getUrlConfig()),d=this.applyLocalVerifyOverride(l),o=new URL(d);o.searchParams.set("blocked",e),t&&o.searchParams.set("portalUrl",t);let g=r||this.config.language;if(g&&o.searchParams.set("lang",g),this.config.mode==="new-tab"){((this.config.newTabTarget||"popup")==="tab"?window.open(o.toString(),"_blank"):window.open(o.toString(),this.brandConstants.popupName,"width=600,height=700"))||(s=(i=this.config).onError)==null||s.call(i,new Error("Failed to open billing notice window. Please check popup blocker settings."));return}this.redirect(o.toString())}catch(l){let d=l instanceof Error?l.message:String(l);(c=(a=this.config).onError)==null||c.call(a,new Error(`Failed to open billing notice: ${d}`))}}getUrlConfig(){return this.brandUrls[this.config.environment]||this.brandUrls.production}getTrustedOrigins(){return this.getUrlConfig().trustedOrigins}getAllowedCustomOrigins(e){let 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(I.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}};I.LOCAL_HOSTNAMES=new Set(["localhost","127.0.0.1","::1"]);var C=I;var le={production:{apiUrl:"https://api.safepassageapp.com",verifyUiUrl:"https://av.safepassageapp.com",engineUrl:"https://engine.safepassageapp.com",wsUrl:"wss://engine.safepassageapp.com/api/websocket/stream",trustedOrigins:["https://av.safepassageapp.com","https://portal.safepassageapp.com","https://api.safepassageapp.com"]},staging:{apiUrl:"https://api.verityav-staging-usw1a.safepassageapp.com",verifyUiUrl:"https://av.verityav-staging-usw1a.safepassageapp.com",engineUrl:"https://engine.verityav-staging-usw1a.safepassageapp.com",wsUrl:"wss://engine.verityav-staging-usw1a.safepassageapp.com/api/websocket/stream",trustedOrigins:["https://av.verityav-staging-usw1a.safepassageapp.com","https://portal.verityav-staging-usw1a.safepassageapp.com","https://api.verityav-staging-usw1a.safepassageapp.com"]}},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.safepassageapp.com"};var m=class extends C{constructor(e){super(e,le,k)}},ce="3.5.0";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/types/base.d.ts
CHANGED
|
@@ -58,6 +58,12 @@ export interface SDKConfig {
|
|
|
58
58
|
* Return false to suppress automatic cancelUrl redirect.
|
|
59
59
|
*/
|
|
60
60
|
onCancel?: () => boolean | void;
|
|
61
|
+
/**
|
|
62
|
+
* Default UI language for verification screens
|
|
63
|
+
* Supported: 'en', 'de', 'es', 'fr'
|
|
64
|
+
* Can be overridden per verification call
|
|
65
|
+
*/
|
|
66
|
+
language?: string;
|
|
61
67
|
/**
|
|
62
68
|
* Callback for errors
|
|
63
69
|
*/
|
|
@@ -94,6 +100,12 @@ export interface VerificationOptions {
|
|
|
94
100
|
* @default false
|
|
95
101
|
*/
|
|
96
102
|
autoReturn?: boolean;
|
|
103
|
+
/**
|
|
104
|
+
* UI language override for this verification
|
|
105
|
+
* Supported: 'en', 'de', 'es', 'fr'
|
|
106
|
+
* Takes precedence over SDKConfig.language
|
|
107
|
+
*/
|
|
108
|
+
language?: string;
|
|
97
109
|
}
|
|
98
110
|
export interface VerificationResult {
|
|
99
111
|
/**
|