@safepassage/sdk 3.4.11 → 3.4.12
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 +5 -5
- package/index.js +21 -15
- package/package.json +2 -2
- package/safepassage.min.js +2 -2
- package/sdk.min.js +2 -2
- package/types/base.d.ts +10 -0
package/README.md
CHANGED
|
@@ -157,7 +157,7 @@ app.get('/age-verified', async (req, res) => {
|
|
|
157
157
|
|
|
158
158
|
// Validate with your SECRET key (sk_...)
|
|
159
159
|
const response = await fetch(
|
|
160
|
-
`https://api.
|
|
160
|
+
`https://api.safepassageapp.com/api/v1/sessions/${sessionId}`,
|
|
161
161
|
{
|
|
162
162
|
headers: {
|
|
163
163
|
'Authorization': `Bearer ${process.env.SAFEPASSAGE_SECRET_KEY}`
|
|
@@ -313,7 +313,7 @@ SafePassage uses two types of API keys:
|
|
|
313
313
|
| Public Key | `pk_` | Client-side SDK (this package) |
|
|
314
314
|
| Secret Key | `sk_` | Server-side validation only |
|
|
315
315
|
|
|
316
|
-
> **Important**: This SDK only works with public keys (`pk_`). For server-side integrations using secret keys, use the [Direct API](https://docs.
|
|
316
|
+
> **Important**: This SDK only works with public keys (`pk_`). For server-side integrations using secret keys, use the [Direct API](https://docs.safepassageapp.com/api) instead.
|
|
317
317
|
|
|
318
318
|
## Browser Support
|
|
319
319
|
|
|
@@ -355,6 +355,6 @@ The SDK now creates sessions automatically via the API when using public keys.
|
|
|
355
355
|
|
|
356
356
|
## Support
|
|
357
357
|
|
|
358
|
-
- [Documentation](https://docs.
|
|
359
|
-
- [API Reference](https://docs.
|
|
360
|
-
- [Dashboard](https://portal.
|
|
358
|
+
- [Documentation](https://docs.safepassageapp.com)
|
|
359
|
+
- [API Reference](https://docs.safepassageapp.com/api)
|
|
360
|
+
- [Dashboard](https://portal.safepassageapp.com)
|
package/index.js
CHANGED
|
@@ -98,6 +98,12 @@ function enforceHTTPS(environment, logLabel = "SDK") {
|
|
|
98
98
|
function validateReturnUrl(url, environment, _logLabel = "SDK") {
|
|
99
99
|
try {
|
|
100
100
|
const parsed = new URL(url);
|
|
101
|
+
if (parsed.protocol === "file:") {
|
|
102
|
+
return {
|
|
103
|
+
isValid: false,
|
|
104
|
+
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."
|
|
105
|
+
};
|
|
106
|
+
}
|
|
101
107
|
if (parsed.protocol !== "https:") {
|
|
102
108
|
const isLocalhost = parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1";
|
|
103
109
|
if (!isLocalhost) {
|
|
@@ -1091,25 +1097,25 @@ var VerificationSDK = _VerificationSDK;
|
|
|
1091
1097
|
// src-redirect/brands/safepassage/urls.ts
|
|
1092
1098
|
var BRAND_URLS = {
|
|
1093
1099
|
production: {
|
|
1094
|
-
apiUrl: "https://api.
|
|
1095
|
-
verifyUiUrl: "https://av.
|
|
1096
|
-
engineUrl: "https://engine.
|
|
1097
|
-
wsUrl: "wss://engine.
|
|
1100
|
+
apiUrl: "https://api.safepassageapp.com",
|
|
1101
|
+
verifyUiUrl: "https://av.safepassageapp.com",
|
|
1102
|
+
engineUrl: "https://engine.safepassageapp.com",
|
|
1103
|
+
wsUrl: "wss://engine.safepassageapp.com/api/websocket/stream",
|
|
1098
1104
|
trustedOrigins: [
|
|
1099
|
-
"https://av.
|
|
1100
|
-
"https://portal.
|
|
1101
|
-
"https://api.
|
|
1105
|
+
"https://av.safepassageapp.com",
|
|
1106
|
+
"https://portal.safepassageapp.com",
|
|
1107
|
+
"https://api.safepassageapp.com"
|
|
1102
1108
|
]
|
|
1103
1109
|
},
|
|
1104
1110
|
staging: {
|
|
1105
|
-
apiUrl: "https://api.staging.
|
|
1106
|
-
verifyUiUrl: "https://av.staging.
|
|
1107
|
-
engineUrl: "https://engine.staging.
|
|
1108
|
-
wsUrl: "wss://engine.staging.
|
|
1111
|
+
apiUrl: "https://api.verityav-staging-usw1a.safepassageapp.com",
|
|
1112
|
+
verifyUiUrl: "https://av.verityav-staging-usw1a.safepassageapp.com",
|
|
1113
|
+
engineUrl: "https://engine.verityav-staging-usw1a.safepassageapp.com",
|
|
1114
|
+
wsUrl: "wss://engine.verityav-staging-usw1a.safepassageapp.com/api/websocket/stream",
|
|
1109
1115
|
trustedOrigins: [
|
|
1110
|
-
"https://av.staging.
|
|
1111
|
-
"https://portal.staging.
|
|
1112
|
-
"https://api.staging.
|
|
1116
|
+
"https://av.verityav-staging-usw1a.safepassageapp.com",
|
|
1117
|
+
"https://portal.verityav-staging-usw1a.safepassageapp.com",
|
|
1118
|
+
"https://api.verityav-staging-usw1a.safepassageapp.com"
|
|
1113
1119
|
]
|
|
1114
1120
|
}
|
|
1115
1121
|
};
|
|
@@ -1120,7 +1126,7 @@ var BRAND_CONSTANTS = {
|
|
|
1120
1126
|
messageType: "safepassage:verification:complete",
|
|
1121
1127
|
legacyMessageType: "safepassage-verification",
|
|
1122
1128
|
popupName: "safepassage-verify",
|
|
1123
|
-
docsUrl: "https://docs.
|
|
1129
|
+
docsUrl: "https://docs.safepassageapp.com"
|
|
1124
1130
|
};
|
|
1125
1131
|
|
|
1126
1132
|
// src-redirect/brands/safepassage/index.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@safepassage/sdk",
|
|
3
|
-
"version": "3.4.
|
|
3
|
+
"version": "3.4.12",
|
|
4
4
|
"description": "SafePassage SDK - Lightweight redirect-based age verification",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"bugs": {
|
|
34
34
|
"url": "https://github.com/safepassageapp/sdk/issues"
|
|
35
35
|
},
|
|
36
|
-
"homepage": "https://docs.
|
|
36
|
+
"homepage": "https://docs.safepassageapp.com/sdk",
|
|
37
37
|
"publishConfig": {
|
|
38
38
|
"access": "public",
|
|
39
39
|
"registry": "https://registry.npmjs.org/"
|
package/safepassage.min.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
/* SafePassage SDK v3.4.
|
|
2
|
-
"use strict";var SafePassageSDK=(()=>{var y=Object.defineProperty,de=Object.defineProperties,ge=Object.getOwnPropertyDescriptor,pe=Object.getOwnPropertyDescriptors,ue=Object.getOwnPropertyNames,v=Object.getOwnPropertySymbols;var T=Object.prototype.hasOwnProperty,W=Object.prototype.propertyIsEnumerable;var B=(n,e,t)=>e in n?y(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t,u=(n,e)=>{for(var t in e||(e={}))T.call(e,t)&&B(n,t,e[t]);if(v)for(var t of v(e))W.call(e,t)&&B(n,t,e[t]);return n},S=(n,e)=>de(n,pe(e));var H=(n,e)=>{var t={};for(var r in n)T.call(n,r)&&e.indexOf(r)<0&&(t[r]=n[r]);if(n!=null&&v)for(var r of v(n))e.indexOf(r)<0&&W.call(n,r)&&(t[r]=n[r]);return t};var A=(n,e)=>()=>(n&&(e=n(n=0)),e);var F=(n,e)=>{for(var t in e)y(n,t,{get:e[t],enumerable:!0})},fe=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of ue(e))!T.call(n,i)&&i!==t&&y(n,i,{get:()=>e[i],enumerable:!(r=ge(e,i))||r.enumerable});return n};var he=n=>fe(y({},"__esModule",{value:!0}),n);function me(n,e){return e.includes(n)}function G(n,e,t=[],r="SDK"){var s;let{origin:i}=n;return me(i,e)||t.length>0&&t.some(a=>{if(a.startsWith("*.")){let l=a.slice(2);return i.endsWith(`.${l}`)||i===`https://${l}`||i===`http://${l}`}return i===a})?!0:(console.warn(`${r} Security: Blocked PostMessage from untrusted origin: ${i}`,{trustedOrigins:e,allowedCustomOrigins:t,eventType:(s=n.data)==null?void 0:s.type}),!1)}function J(n,e,t,r){let{data:i}=n;return!i||typeof i!="object"?{isValid:!1,error:"Invalid message format"}:(r?[t,r]:[t]).includes(i.type)?!i.sessionId||i.sessionId!==e?{isValid:!1,error:"Session ID mismatch"}:!i.status||!["verified","failed","cancelled"].includes(i.status)?{isValid:!1,error:"Invalid status value"}:{isValid:!0}:{isValid:!1,error:"Invalid message type"}}function X(n,e="SDK"){n==="production"&&window.location.protocol!=="https:"&&console.warn(`${e} Warning: HTTPS recommended for production environment`,{current:window.location.href})}function x(n,e,t="SDK"){try{let r=new URL(n);if(r.protocol!=="https:"&&!(r.hostname==="localhost"||r.hostname==="127.0.0.1"))return{isValid:!1,error:`HTTPS required for return URLs in ${e}`};let i=[/data:/i,/javascript:/i,/vbscript:/i,/file:/i,/ftp:/i];for(let s of i)if(s.test(n))return{isValid:!1,error:"Blocked suspicious URL scheme"};return{isValid:!0}}catch(r){return{isValid:!1,error:"Invalid URL format"}}}function g(n,e,t="SDK"){let r={timestamp:new Date().toISOString(),userAgent:typeof navigator!="undefined"?navigator.userAgent:"unknown",url:typeof window!="undefined"?window.location.href:"unknown"};console.warn(`${t} Security Event: ${n}`,u(u({},r),e))}var P,Y,R=A(()=>{"use strict";P=class{constructor(){this.attempts=new Map;this.maxAttempts=5;this.timeWindow=6e4}isAllowed(e,t="SDK"){let r=Date.now(),s=(this.attempts.get(e)||[]).filter(o=>r-o<this.timeWindow);return s.length>=this.maxAttempts?(console.warn(`${t} Security: Rate limit exceeded for ${e}`),!1):(s.push(r),this.attempts.set(e,s),!0)}reset(e){this.attempts.delete(e)}},Y=new P});var Q={};F(Q,{createSignedState:()=>ve,generateHMAC:()=>V,generateSecureToken:()=>Z,parseSignedState:()=>ye,verifyHMAC:()=>z});async function V(n,e){let t=new TextEncoder,r=t.encode(e),i=t.encode(n),s=await crypto.subtle.importKey("raw",r,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),o=await crypto.subtle.sign("HMAC",s,i);return Array.from(new Uint8Array(o)).map(a=>a.toString(16).padStart(2,"0")).join("")}async function z(n,e,t){try{let r=await V(n,t);return we(e,r)}catch(r){return!1}}function we(n,e){if(n.length!==e.length)return!1;let t=0;for(let r=0;r<n.length;r++)t|=n.charCodeAt(r)^e.charCodeAt(r);return t===0}function Z(n=32){let e=new Uint8Array(n);return crypto.getRandomValues(e),Array.from(e,t=>t.toString(16).padStart(2,"0")).join("")}async function ve(n,e){let t=S(u({},n),{timestamp:Date.now(),nonce:Z(16)}),r=JSON.stringify(t),i=await V(r,e);return btoa(JSON.stringify({data:t,signature:i}))}async function ye(n,e,t=te,r="SDK"){try{let s=atob(n),o=JSON.parse(s);if(!o.data||!o.signature)return console.warn(`${r}: Invalid signed state format`),null;let{data:a,signature:l}=o,d=JSON.stringify(a);if(!await z(d,l,e))return console.warn(`${r}: State signature verification failed`),null;if(a.timestamp){let h=Date.now()-a.timestamp;if(h>t)return console.warn(`${r}: State parameter expired`,{age:h,maxAge:t}),null}let i=a,{timestamp:p,nonce:f}=i;return H(i,["timestamp","nonce"])}catch(s){return console.warn(`${r}: Failed to parse signed state`,s),null}}var ee=A(()=>{"use strict";L()});function se(n,e){if(!n.apiKey)throw new Error("apiKey is required");if(n.apiKey.length>ie)throw new Error(`apiKey exceeds maximum length of ${ie} characters`);if(!Se.test(n.apiKey))throw new Error("Invalid apiKey format. Expected pk_xxx (public) or sk_xxx (private)");if(typeof window!="undefined"&&n.apiKey.startsWith("sk_"))throw new Error(`Secret keys (sk_) should never be used in browser code for security reasons. Secret keys expose your account to unauthorized access if used client-side. Please use your public key (pk_) instead. If you need to use features that require a secret key (like custom challenge age), create the session server-side and pass the sessionId to startVerificationWithSession(). See: ${e.docsUrl}/server-side-sessions`);if(!n.returnUrl)throw new Error("returnUrl is required");if(n.returnUrl.length>U)throw new Error(`returnUrl exceeds maximum length of ${U} characters`);let t=Ue(),r=x(n.returnUrl,t,e.brandName);if(!r.isValid)throw new Error(`returnUrl validation failed: ${r.error}`);if(n.cancelUrl){if(n.cancelUrl.length>U)throw new Error(`cancelUrl exceeds maximum length of ${U} characters`);let i=x(n.cancelUrl,t,e.brandName);if(!i.isValid)throw new Error(`cancelUrl validation failed: ${i.error}`)}if(n.defaultChallengeAge!==void 0){if(n.defaultChallengeAge<ne)throw new Error(`defaultChallengeAge must be at least ${ne}`);if(n.defaultChallengeAge>re)throw new Error(`defaultChallengeAge cannot exceed ${re}`)}if(n.defaultVerificationMode&&!["L1","L2"].includes(n.defaultVerificationMode))throw new Error("defaultVerificationMode must be L1 or L2");if(n.mode&&!["redirect","new-tab"].includes(n.mode))throw new Error("mode must be redirect or new-tab");if(n.newTabTarget&&!["popup","tab"].includes(n.newTabTarget))throw new Error("newTabTarget must be popup or tab")}function Ue(){if(typeof window=="undefined")return"production";let n=window.location.hostname;return n.includes("staging")||n.includes("stage")?"staging":"production"}async function oe(n,e,t,r="SDK"){let{createSignedState:i}=await Promise.resolve().then(()=>(ee(),Q));return i(n,t)}var ne,re,U,ie,te,Se,L=A(()=>{"use strict";R();ne=25,re=150,U=2048,ie=128,te=6e5,Se=/^(pk_|sk_)[a-zA-Z0-9_]+$/});var Ce={};F(Ce,{SafePassage:()=>m,VERSION:()=>ce,default:()=>be});function q(){crypto.randomUUID||(crypto.randomUUID=function(){let n=new Uint8Array(16);crypto.getRandomValues(n),n[6]=n[6]&15|64,n[8]=n[8]&63|128;let e=Array.from(n).map(t=>t.toString(16).padStart(2,"0")).join("");return[e.slice(0,8),e.slice(8,12),e.slice(12,16),e.slice(16,20),e.slice(20,32)].join("-")})}function j(n="SDK"){let e=[];if(!window.crypto||!window.crypto.getRandomValues)throw new Error(`${n} requires Web Crypto API support`);if(!window.crypto.subtle)throw new Error(`${n} requires Web Crypto subtle API for HMAC operations`);crypto.randomUUID||e.push("crypto.randomUUID not supported, using polyfill"),window.URLSearchParams||e.push("URLSearchParams not supported, consider adding a polyfill for IE 11 support"),e.length>0&&console.warn(`${n} Browser Compatibility:`,e.join("; "))}L();function E(n,e){let t=e.verifyUiUrl;if(!t||!t.startsWith("https://"))throw new Error(`HTTPS required for ${n} environment`);return t}function Ee(n,e){let t=e.apiUrl;if(!t||!t.startsWith("https://"))throw new Error(`HTTPS required for API URLs in ${n} environment`);return t}function ae(n,e,t="SDK"){let r=window.location.protocol==="https:";switch(n){case"production":r||console.warn(`${t} Warning: HTTPS recommended for production environment`);break;case"staging":r||console.warn(`${t} Warning: HTTPS strongly recommended in staging environment`);break}try{E(n,e),Ee(n,e)}catch(i){let s=i instanceof Error?i.message:String(i);throw new Error(`Environment configuration validation failed: ${s}`)}}R();var C=class C{constructor(e,t,r){this.popupWindow=null;this.messageListener=null;this.popupMonitorInterval=null;this.unloadListener=null;this.isVerificationInProgress=!1;this.currentSessionId=null;this.hasReceivedResult=!1;this.lastVerifyUrl=null;this.lastSessionToken=null;this.lastExternalUserId=null;this.lastSandboxMode=null;this.temporaryHandoffToken=null;this.brandUrls=t,this.brandConstants=r,se(e,{brandName:this.brandConstants.name,docsUrl:this.brandConstants.docsUrl});let i=e.environment||this.detectEnvironment();i!=="staging"&&i!=="production"&&(console.warn(`${this.brandConstants.name} SDK: Unknown environment '${i}', defaulting to 'production'`),i="production"),this.config=S(u({},e),{environment:i,mode:e.mode||"redirect",newTabTarget:e.newTabTarget||"popup"}),ae(this.config.environment,this.getUrlConfig(),this.brandConstants.name),X(this.config.environment,this.brandConstants.name),g("SDK_INITIALIZED",{environment:this.config.environment,mode:this.config.mode,origin:window.location.origin,protocol:window.location.protocol,hostname:window.location.hostname},this.brandConstants.name),this.setupAutoCleanup()}async verify(e={}){var i,s,o,a,l,d;let t=this.isPublicKey(),r;if(t)r=await this.createInternalSession(e);else throw new Error("Private API keys (sk_) should use the direct API, not the SDK. The SDK is designed for browser-based public key usage only.");if(!r)throw new Error("Failed to obtain sessionId from server");if(this.isVerificationInProgress){let c=new Error(`Verification already in progress for session ${(i=this.currentSessionId)==null?void 0:i.substring(0,8)}...`);throw g("RACE_CONDITION_PREVENTED",{currentSession:((s=this.currentSessionId)==null?void 0:s.substring(0,8))+"...",attemptedSession:"new-session-attempt",origin:window.location.origin},this.brandConstants.name),(a=(o=this.config).onError)==null||a.call(o,c),c}this.isVerificationInProgress=!0,this.currentSessionId=r,this.lastExternalUserId=e.externalUserId||null;try{let c=`${this.config.apiKey}:${window.location.origin}`;if(!Y.isAllowed(c,this.brandConstants.name)){let f=new Error("Too many verification attempts. Please wait before trying again.");throw g("RATE_LIMIT_EXCEEDED",{apiKey:this.config.apiKey.substring(0,8)+"...",origin:window.location.origin,sessionId:r?r.substring(0,8)+"...":"undefined"},this.brandConstants.name),(d=(l=this.config).onError)==null||d.call(l,f),f}let p=await this.buildVerificationUrl(e,r);g("VERIFICATION_INITIATED",{environment:this.config.environment,mode:this.config.mode,sessionId:r?r.substring(0,8)+"...":"undefined",origin:window.location.origin},this.brandConstants.name),this.config.mode==="new-tab"?this.openNewTab(p,r):(this.unlockVerification(),this.redirect(p))}catch(c){throw this.unlockVerification(),c}}async buildVerificationUrl(e,t){var d;let r=this.config.verifyUrl||E(this.config.environment,this.getUrlConfig()),i=e.challengeAge!==void 0,s=e.verificationMode!==void 0,o=i||s,a=await oe({merchantId:this.config.apiKey,sessionId:t,returnUrl:this.config.returnUrl,cancelUrl:this.config.cancelUrl,challengeAge:e.challengeAge||this.config.defaultChallengeAge,verificationMode:e.verificationMode||this.config.defaultVerificationMode,hasOverrides:o,externalUserId:e.externalUserId,timestamp:Date.now(),apiUrl:this.getPortalApiUrl(),engineUrl:this.getEngineUrl(),wsUrl:this.getWebSocketUrl(),environment:this.config.environment,features:{testMode:!1,warmupPeriodMs:500,qualityThreshold:.6,sandboxMode:(d=this.lastSandboxMode)!=null?d:!1},handoffToken:this.temporaryHandoffToken||void 0,sessionToken:this.lastSessionToken||void 0,verifyUrl:this.lastVerifyUrl||void 0},this.config.environment,this.getHmacSecret(),this.brandConstants.name);if(this.lastVerifyUrl)try{let c=this.applyLocalVerifyOverride(this.lastVerifyUrl),p=new URL(c);return p.searchParams.set("state",a),p.searchParams.set("mode",this.config.mode),e.skipIntro&&p.searchParams.set("skip_intro","true"),e.autoReturn&&p.searchParams.set("auto_return","true"),p.toString()}catch(c){}let l=new URLSearchParams({state:a,sessionId:t,mode:this.config.mode});return e.skipIntro&&l.set("skip_intro","true"),e.autoReturn&&l.set("auto_return","true"),`${r}/?${l.toString()}`}redirect(e){window.location.href=e}openNewTab(e,t){var l,d;if(this.cleanup(),this.hasReceivedResult=!1,this.popupMonitorInterval&&(clearInterval(this.popupMonitorInterval),this.popupMonitorInterval=null),(this.config.newTabTarget||"popup")==="tab"?this.popupWindow=window.open(e,"_blank"):this.popupWindow=window.open(e,this.brandConstants.popupName,"width=600,height=700"),!this.popupWindow){(d=(l=this.config).onError)==null||d.call(l,new Error("Failed to open verification window. Please check popup blocker settings."));return}let i=this.getTrustedOrigins(),s=this.getAllowedCustomOrigins(e),o=this.brandConstants.messageType,a=this.brandConstants.legacyMessageType;this.messageListener=c=>{var D,M,_,O,$,N,K;let p=(D=c.data)==null?void 0:D.type;if(!p||typeof p!="string"||!(a?[o,a]:[o]).includes(p))return;if(!G(c,i,s,this.brandConstants.name)){g("POSTMESSAGE_ORIGIN_BLOCKED",{origin:c.origin,environment:this.config.environment,expectedOrigins:`${this.brandConstants.name} trusted origins for ${this.config.environment}`,messageType:(M=c.data)==null?void 0:M.type},this.brandConstants.name);return}let I=J(c,t,o,a);if(!I.isValid){g("POSTMESSAGE_VALIDATION_FAILED",{error:I.error,origin:c.origin,sessionId:t.substring(0,8)+"...",messageType:(_=c.data)==null?void 0:_.type},this.brandConstants.name);return}let h=c.data.status;if(h==="cancelled"){this.handleCancellation(t,"postmessage");return}let w={sessionId:c.data.sessionId,status:h,timestamp:c.data.timestamp,externalUserId:c.data.externalUserId};this.hasReceivedResult=!0,g("VERIFICATION_COMPLETED",{status:w.status,sessionId:t.substring(0,8)+"...",origin:c.origin},this.brandConstants.name),this.cleanup({closePopup:!1}),this.unlockVerification(),this.popupMonitorInterval&&(clearInterval(this.popupMonitorInterval),this.popupMonitorInterval=null),w.status==="verified"?($=(O=this.config).onComplete)==null||$.call(O,w):(K=(N=this.config).onError)==null||K.call(N,new Error(`Verification failed: ${w.status}`))},window.addEventListener("message",this.messageListener),this.popupMonitorInterval=setInterval(()=>{this.popupWindow&&this.popupWindow.closed&&(g("POPUP_CLOSED_BY_USER",{sessionId:t.substring(0,8)+"...",environment:this.config.environment},this.brandConstants.name),this.hasReceivedResult||this.handleCancellation(t,"popup-closed"))},500)}setupAutoCleanup(){if(this.unloadListener=()=>{g("SDK_AUTO_CLEANUP",{environment:this.config.environment,trigger:"page_unload"},this.brandConstants.name),this.cleanup(),this.unlockVerification()},window.addEventListener("beforeunload",this.unloadListener),window.addEventListener("pagehide",this.unloadListener),window.history&&window.history.pushState){let e=window.history.pushState;window.history.pushState=(...t)=>(this.cleanup(),this.unlockVerification(),e.apply(window.history,t))}}detectEnvironment(){let e=window.location.hostname;return e.includes("staging")||e.includes("stage")?"staging":"production"}getEnvironment(){return this.config.environment}unlockVerification(){this.isVerificationInProgress=!1,this.currentSessionId=null,g("VERIFICATION_UNLOCKED",{environment:this.config.environment,origin:window.location.origin},this.brandConstants.name)}cleanup(e={}){let t=e.closePopup!==!1;this.popupWindow&&(t&&!this.popupWindow.closed&&this.popupWindow.close(),(t||this.popupWindow.closed)&&(this.popupWindow=null)),this.messageListener&&(window.removeEventListener("message",this.messageListener),this.messageListener=null),this.popupMonitorInterval&&(clearInterval(this.popupMonitorInterval),this.popupMonitorInterval=null)}handleCancellation(e,t){if(this.hasReceivedResult)return;this.hasReceivedResult=!0,g("VERIFICATION_CANCELLED",{source:t,sessionId:e.substring(0,8)+"...",environment:this.config.environment,origin:window.location.origin},this.brandConstants.name),this.cleanup(),this.unlockVerification();let r=!0;if(this.config.onCancel)try{this.config.onCancel()===!1&&(r=!1)}catch(i){g("CANCEL_CALLBACK_FAILED",{error:i instanceof Error?i.message:String(i),sessionId:e.substring(0,8)+"..."},this.brandConstants.name)}r&&this.redirectToCancelUrl(e)}redirectToCancelUrl(e){if(this.config.cancelUrl)try{let t=decodeURIComponent(this.config.cancelUrl),r=new URL(t);r.searchParams.set("sessionId",e),r.searchParams.set("status","cancelled"),r.searchParams.set("timestamp",Date.now().toString()),this.lastExternalUserId&&r.searchParams.set("externalUserId",this.lastExternalUserId),window.location.href=r.toString()}catch(t){g("CANCEL_REDIRECT_FAILED",{error:t instanceof Error?t.message:String(t),sessionId:e.substring(0,8)+"..."},this.brandConstants.name)}}removeAutoCleanupListeners(){this.unloadListener&&(window.removeEventListener("beforeunload",this.unloadListener),window.removeEventListener("pagehide",this.unloadListener),this.unloadListener=null)}destroy(){g("SDK_DESTROYED",{environment:this.config.environment,origin:window.location.origin},this.brandConstants.name),this.cleanup(),this.unlockVerification(),this.removeAutoCleanupListeners()}getPortalApiUrl(){return this.config.apiUrl?this.config.apiUrl:this.getUrlConfig().apiUrl}getEngineUrl(){return this.getUrlConfig().engineUrl}getWebSocketUrl(){return this.getUrlConfig().wsUrl}isPublicKey(){return this.config.apiKey.startsWith("pk_")}async createInternalSession(e){var t,r;try{let i=this.getPortalApiUrl(),s=await fetch(`${i}/api/v1/sessions/create`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.config.apiKey}`},body:JSON.stringify({merchantId:this.config.apiKey,returnUrl:this.config.returnUrl,cancelUrl:this.config.cancelUrl,challengeAge:e.challengeAge,verificationMode:e.verificationMode,merchantName:document.title||window.location.hostname,externalUserId:e.externalUserId})});if(!s.ok){let l=await s.json().catch(()=>({})),d=l==null?void 0:l.code;throw this.isBillingBlockError(d)&&this.openBillingBlockPage(d,l==null?void 0:l.portalUrl),new Error(`Failed to create session: ${s.status} ${s.statusText}. ${l.message||""}`)}let o=await s.json(),a=o.sessionId;if(!a)throw new Error("Server did not return a sessionId");return o.verifyUrl&&(this.lastVerifyUrl=o.verifyUrl),o.sessionToken&&(this.lastSessionToken=o.sessionToken),o.handoffToken&&(this.temporaryHandoffToken=o.handoffToken),typeof o.sandboxMode=="boolean"?this.lastSandboxMode=o.sandboxMode:this.lastSandboxMode=null,g("INTERNAL_SESSION_CREATED",{sessionId:a.substring(0,8)+"...",environment:this.config.environment,apiKeyType:"public"},this.brandConstants.name),a}catch(i){let s=i instanceof Error?i.message:String(i);throw g("INTERNAL_SESSION_FAILED",{error:s,environment:this.config.environment,apiKeyType:"public"},this.brandConstants.name),(r=(t=this.config).onError)==null||r.call(t,i),new Error(`Failed to create verification session: ${s}`)}}isBillingBlockError(e){return e==="SUBSCRIPTION_REQUIRED"||e==="PLAN_LIMIT_REACHED"||e==="SANDBOX_LIMIT_REACHED"}openBillingBlockPage(e,t){var r,i,s,o;try{let a=this.config.verifyUrl||E(this.config.environment,this.getUrlConfig()),l=this.applyLocalVerifyOverride(a),d=new URL(l);if(d.searchParams.set("blocked",e),t&&d.searchParams.set("portalUrl",t),this.config.mode==="new-tab"){((this.config.newTabTarget||"popup")==="tab"?window.open(d.toString(),"_blank"):window.open(d.toString(),this.brandConstants.popupName,"width=600,height=700"))||(i=(r=this.config).onError)==null||i.call(r,new Error("Failed to open billing notice window. Please check popup blocker settings."));return}this.redirect(d.toString())}catch(a){let l=a instanceof Error?a.message:String(a);(o=(s=this.config).onError)==null||o.call(s,new Error(`Failed to open billing notice: ${l}`))}}getUrlConfig(){return this.brandUrls[this.config.environment]||this.brandUrls.production}getTrustedOrigins(){return this.getUrlConfig().trustedOrigins}getAllowedCustomOrigins(e){let t=new Set,r=this.getLocalOrigin(this.config.verifyUrl||null),i=this.getLocalOrigin(e||null);return r&&t.add(r),i&&t.add(i),Array.from(t)}getLocalOrigin(e){if(!e)return null;try{let t=new URL(e);if(C.LOCAL_HOSTNAMES.has(t.hostname))return t.origin}catch(t){return null}return null}applyLocalVerifyOverride(e){let t=this.getLocalOrigin(this.config.verifyUrl||null);if(!t)return e;try{let r=new URL(t),i=new URL(e);return i.protocol=r.protocol,i.host=r.host,i.toString()}catch(r){return e}}getHmacSecret(){return this.config.environment==="staging"?this.brandConstants.hmacSecretStaging:this.brandConstants.hmacSecretProd}};C.LOCAL_HOSTNAMES=new Set(["localhost","127.0.0.1","::1"]);var b=C;var le={production:{apiUrl:"https://api.safepassage.live",verifyUiUrl:"https://av.safepassage.live",engineUrl:"https://engine.safepassage.live",wsUrl:"wss://engine.safepassage.live/api/websocket/stream",trustedOrigins:["https://av.safepassage.live","https://portal.safepassage.live","https://api.safepassage.live"]},staging:{apiUrl:"https://api.staging.safepassage.live",verifyUiUrl:"https://av.staging.safepassage.live",engineUrl:"https://engine.staging.safepassage.live",wsUrl:"wss://engine.staging.safepassage.live/api/websocket/stream",trustedOrigins:["https://av.staging.safepassage.live","https://portal.staging.safepassage.live","https://api.staging.safepassage.live"]}},k={name:"SafePassage",hmacSecretProd:"safepassage-prod-hmac-2025",hmacSecretStaging:"safepassage-stage-hmac-2025",messageType:"safepassage:verification:complete",legacyMessageType:"safepassage-verification",popupName:"safepassage-verify",docsUrl:"https://docs.safepassage.live"};var m=class extends b{constructor(e){super(e,le,k)}},ce="3.4.9";m.VERSION=ce;typeof window!="undefined"&&(q(),j(`${k.name} SDK`));var be=m;return he(Ce);})();
|
|
1
|
+
/* SafePassage SDK v3.4.12 */
|
|
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);})();
|
|
3
3
|
if(typeof SafePassageSDK !== "undefined" && SafePassageSDK.SafePassage) { window.SafePassage = SafePassageSDK.SafePassage; window.SafePassage.VERSION = SafePassageSDK.VERSION; }
|
package/sdk.min.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
/* SafePassage SDK v3.4.
|
|
2
|
-
"use strict";var SafePassageSDK=(()=>{var y=Object.defineProperty,de=Object.defineProperties,ge=Object.getOwnPropertyDescriptor,pe=Object.getOwnPropertyDescriptors,ue=Object.getOwnPropertyNames,v=Object.getOwnPropertySymbols;var T=Object.prototype.hasOwnProperty,W=Object.prototype.propertyIsEnumerable;var B=(n,e,t)=>e in n?y(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t,u=(n,e)=>{for(var t in e||(e={}))T.call(e,t)&&B(n,t,e[t]);if(v)for(var t of v(e))W.call(e,t)&&B(n,t,e[t]);return n},S=(n,e)=>de(n,pe(e));var H=(n,e)=>{var t={};for(var r in n)T.call(n,r)&&e.indexOf(r)<0&&(t[r]=n[r]);if(n!=null&&v)for(var r of v(n))e.indexOf(r)<0&&W.call(n,r)&&(t[r]=n[r]);return t};var A=(n,e)=>()=>(n&&(e=n(n=0)),e);var F=(n,e)=>{for(var t in e)y(n,t,{get:e[t],enumerable:!0})},fe=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of ue(e))!T.call(n,i)&&i!==t&&y(n,i,{get:()=>e[i],enumerable:!(r=ge(e,i))||r.enumerable});return n};var he=n=>fe(y({},"__esModule",{value:!0}),n);function me(n,e){return e.includes(n)}function G(n,e,t=[],r="SDK"){var s;let{origin:i}=n;return me(i,e)||t.length>0&&t.some(a=>{if(a.startsWith("*.")){let l=a.slice(2);return i.endsWith(`.${l}`)||i===`https://${l}`||i===`http://${l}`}return i===a})?!0:(console.warn(`${r} Security: Blocked PostMessage from untrusted origin: ${i}`,{trustedOrigins:e,allowedCustomOrigins:t,eventType:(s=n.data)==null?void 0:s.type}),!1)}function J(n,e,t,r){let{data:i}=n;return!i||typeof i!="object"?{isValid:!1,error:"Invalid message format"}:(r?[t,r]:[t]).includes(i.type)?!i.sessionId||i.sessionId!==e?{isValid:!1,error:"Session ID mismatch"}:!i.status||!["verified","failed","cancelled"].includes(i.status)?{isValid:!1,error:"Invalid status value"}:{isValid:!0}:{isValid:!1,error:"Invalid message type"}}function X(n,e="SDK"){n==="production"&&window.location.protocol!=="https:"&&console.warn(`${e} Warning: HTTPS recommended for production environment`,{current:window.location.href})}function x(n,e,t="SDK"){try{let r=new URL(n);if(r.protocol!=="https:"&&!(r.hostname==="localhost"||r.hostname==="127.0.0.1"))return{isValid:!1,error:`HTTPS required for return URLs in ${e}`};let i=[/data:/i,/javascript:/i,/vbscript:/i,/file:/i,/ftp:/i];for(let s of i)if(s.test(n))return{isValid:!1,error:"Blocked suspicious URL scheme"};return{isValid:!0}}catch(r){return{isValid:!1,error:"Invalid URL format"}}}function g(n,e,t="SDK"){let r={timestamp:new Date().toISOString(),userAgent:typeof navigator!="undefined"?navigator.userAgent:"unknown",url:typeof window!="undefined"?window.location.href:"unknown"};console.warn(`${t} Security Event: ${n}`,u(u({},r),e))}var P,Y,R=A(()=>{"use strict";P=class{constructor(){this.attempts=new Map;this.maxAttempts=5;this.timeWindow=6e4}isAllowed(e,t="SDK"){let r=Date.now(),s=(this.attempts.get(e)||[]).filter(o=>r-o<this.timeWindow);return s.length>=this.maxAttempts?(console.warn(`${t} Security: Rate limit exceeded for ${e}`),!1):(s.push(r),this.attempts.set(e,s),!0)}reset(e){this.attempts.delete(e)}},Y=new P});var Q={};F(Q,{createSignedState:()=>ve,generateHMAC:()=>V,generateSecureToken:()=>Z,parseSignedState:()=>ye,verifyHMAC:()=>z});async function V(n,e){let t=new TextEncoder,r=t.encode(e),i=t.encode(n),s=await crypto.subtle.importKey("raw",r,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),o=await crypto.subtle.sign("HMAC",s,i);return Array.from(new Uint8Array(o)).map(a=>a.toString(16).padStart(2,"0")).join("")}async function z(n,e,t){try{let r=await V(n,t);return we(e,r)}catch(r){return!1}}function we(n,e){if(n.length!==e.length)return!1;let t=0;for(let r=0;r<n.length;r++)t|=n.charCodeAt(r)^e.charCodeAt(r);return t===0}function Z(n=32){let e=new Uint8Array(n);return crypto.getRandomValues(e),Array.from(e,t=>t.toString(16).padStart(2,"0")).join("")}async function ve(n,e){let t=S(u({},n),{timestamp:Date.now(),nonce:Z(16)}),r=JSON.stringify(t),i=await V(r,e);return btoa(JSON.stringify({data:t,signature:i}))}async function ye(n,e,t=te,r="SDK"){try{let s=atob(n),o=JSON.parse(s);if(!o.data||!o.signature)return console.warn(`${r}: Invalid signed state format`),null;let{data:a,signature:l}=o,d=JSON.stringify(a);if(!await z(d,l,e))return console.warn(`${r}: State signature verification failed`),null;if(a.timestamp){let h=Date.now()-a.timestamp;if(h>t)return console.warn(`${r}: State parameter expired`,{age:h,maxAge:t}),null}let i=a,{timestamp:p,nonce:f}=i;return H(i,["timestamp","nonce"])}catch(s){return console.warn(`${r}: Failed to parse signed state`,s),null}}var ee=A(()=>{"use strict";L()});function se(n,e){if(!n.apiKey)throw new Error("apiKey is required");if(n.apiKey.length>ie)throw new Error(`apiKey exceeds maximum length of ${ie} characters`);if(!Se.test(n.apiKey))throw new Error("Invalid apiKey format. Expected pk_xxx (public) or sk_xxx (private)");if(typeof window!="undefined"&&n.apiKey.startsWith("sk_"))throw new Error(`Secret keys (sk_) should never be used in browser code for security reasons. Secret keys expose your account to unauthorized access if used client-side. Please use your public key (pk_) instead. If you need to use features that require a secret key (like custom challenge age), create the session server-side and pass the sessionId to startVerificationWithSession(). See: ${e.docsUrl}/server-side-sessions`);if(!n.returnUrl)throw new Error("returnUrl is required");if(n.returnUrl.length>U)throw new Error(`returnUrl exceeds maximum length of ${U} characters`);let t=Ue(),r=x(n.returnUrl,t,e.brandName);if(!r.isValid)throw new Error(`returnUrl validation failed: ${r.error}`);if(n.cancelUrl){if(n.cancelUrl.length>U)throw new Error(`cancelUrl exceeds maximum length of ${U} characters`);let i=x(n.cancelUrl,t,e.brandName);if(!i.isValid)throw new Error(`cancelUrl validation failed: ${i.error}`)}if(n.defaultChallengeAge!==void 0){if(n.defaultChallengeAge<ne)throw new Error(`defaultChallengeAge must be at least ${ne}`);if(n.defaultChallengeAge>re)throw new Error(`defaultChallengeAge cannot exceed ${re}`)}if(n.defaultVerificationMode&&!["L1","L2"].includes(n.defaultVerificationMode))throw new Error("defaultVerificationMode must be L1 or L2");if(n.mode&&!["redirect","new-tab"].includes(n.mode))throw new Error("mode must be redirect or new-tab");if(n.newTabTarget&&!["popup","tab"].includes(n.newTabTarget))throw new Error("newTabTarget must be popup or tab")}function Ue(){if(typeof window=="undefined")return"production";let n=window.location.hostname;return n.includes("staging")||n.includes("stage")?"staging":"production"}async function oe(n,e,t,r="SDK"){let{createSignedState:i}=await Promise.resolve().then(()=>(ee(),Q));return i(n,t)}var ne,re,U,ie,te,Se,L=A(()=>{"use strict";R();ne=25,re=150,U=2048,ie=128,te=6e5,Se=/^(pk_|sk_)[a-zA-Z0-9_]+$/});var Ce={};F(Ce,{SafePassage:()=>m,VERSION:()=>ce,default:()=>be});function q(){crypto.randomUUID||(crypto.randomUUID=function(){let n=new Uint8Array(16);crypto.getRandomValues(n),n[6]=n[6]&15|64,n[8]=n[8]&63|128;let e=Array.from(n).map(t=>t.toString(16).padStart(2,"0")).join("");return[e.slice(0,8),e.slice(8,12),e.slice(12,16),e.slice(16,20),e.slice(20,32)].join("-")})}function j(n="SDK"){let e=[];if(!window.crypto||!window.crypto.getRandomValues)throw new Error(`${n} requires Web Crypto API support`);if(!window.crypto.subtle)throw new Error(`${n} requires Web Crypto subtle API for HMAC operations`);crypto.randomUUID||e.push("crypto.randomUUID not supported, using polyfill"),window.URLSearchParams||e.push("URLSearchParams not supported, consider adding a polyfill for IE 11 support"),e.length>0&&console.warn(`${n} Browser Compatibility:`,e.join("; "))}L();function E(n,e){let t=e.verifyUiUrl;if(!t||!t.startsWith("https://"))throw new Error(`HTTPS required for ${n} environment`);return t}function Ee(n,e){let t=e.apiUrl;if(!t||!t.startsWith("https://"))throw new Error(`HTTPS required for API URLs in ${n} environment`);return t}function ae(n,e,t="SDK"){let r=window.location.protocol==="https:";switch(n){case"production":r||console.warn(`${t} Warning: HTTPS recommended for production environment`);break;case"staging":r||console.warn(`${t} Warning: HTTPS strongly recommended in staging environment`);break}try{E(n,e),Ee(n,e)}catch(i){let s=i instanceof Error?i.message:String(i);throw new Error(`Environment configuration validation failed: ${s}`)}}R();var C=class C{constructor(e,t,r){this.popupWindow=null;this.messageListener=null;this.popupMonitorInterval=null;this.unloadListener=null;this.isVerificationInProgress=!1;this.currentSessionId=null;this.hasReceivedResult=!1;this.lastVerifyUrl=null;this.lastSessionToken=null;this.lastExternalUserId=null;this.lastSandboxMode=null;this.temporaryHandoffToken=null;this.brandUrls=t,this.brandConstants=r,se(e,{brandName:this.brandConstants.name,docsUrl:this.brandConstants.docsUrl});let i=e.environment||this.detectEnvironment();i!=="staging"&&i!=="production"&&(console.warn(`${this.brandConstants.name} SDK: Unknown environment '${i}', defaulting to 'production'`),i="production"),this.config=S(u({},e),{environment:i,mode:e.mode||"redirect",newTabTarget:e.newTabTarget||"popup"}),ae(this.config.environment,this.getUrlConfig(),this.brandConstants.name),X(this.config.environment,this.brandConstants.name),g("SDK_INITIALIZED",{environment:this.config.environment,mode:this.config.mode,origin:window.location.origin,protocol:window.location.protocol,hostname:window.location.hostname},this.brandConstants.name),this.setupAutoCleanup()}async verify(e={}){var i,s,o,a,l,d;let t=this.isPublicKey(),r;if(t)r=await this.createInternalSession(e);else throw new Error("Private API keys (sk_) should use the direct API, not the SDK. The SDK is designed for browser-based public key usage only.");if(!r)throw new Error("Failed to obtain sessionId from server");if(this.isVerificationInProgress){let c=new Error(`Verification already in progress for session ${(i=this.currentSessionId)==null?void 0:i.substring(0,8)}...`);throw g("RACE_CONDITION_PREVENTED",{currentSession:((s=this.currentSessionId)==null?void 0:s.substring(0,8))+"...",attemptedSession:"new-session-attempt",origin:window.location.origin},this.brandConstants.name),(a=(o=this.config).onError)==null||a.call(o,c),c}this.isVerificationInProgress=!0,this.currentSessionId=r,this.lastExternalUserId=e.externalUserId||null;try{let c=`${this.config.apiKey}:${window.location.origin}`;if(!Y.isAllowed(c,this.brandConstants.name)){let f=new Error("Too many verification attempts. Please wait before trying again.");throw g("RATE_LIMIT_EXCEEDED",{apiKey:this.config.apiKey.substring(0,8)+"...",origin:window.location.origin,sessionId:r?r.substring(0,8)+"...":"undefined"},this.brandConstants.name),(d=(l=this.config).onError)==null||d.call(l,f),f}let p=await this.buildVerificationUrl(e,r);g("VERIFICATION_INITIATED",{environment:this.config.environment,mode:this.config.mode,sessionId:r?r.substring(0,8)+"...":"undefined",origin:window.location.origin},this.brandConstants.name),this.config.mode==="new-tab"?this.openNewTab(p,r):(this.unlockVerification(),this.redirect(p))}catch(c){throw this.unlockVerification(),c}}async buildVerificationUrl(e,t){var d;let r=this.config.verifyUrl||E(this.config.environment,this.getUrlConfig()),i=e.challengeAge!==void 0,s=e.verificationMode!==void 0,o=i||s,a=await oe({merchantId:this.config.apiKey,sessionId:t,returnUrl:this.config.returnUrl,cancelUrl:this.config.cancelUrl,challengeAge:e.challengeAge||this.config.defaultChallengeAge,verificationMode:e.verificationMode||this.config.defaultVerificationMode,hasOverrides:o,externalUserId:e.externalUserId,timestamp:Date.now(),apiUrl:this.getPortalApiUrl(),engineUrl:this.getEngineUrl(),wsUrl:this.getWebSocketUrl(),environment:this.config.environment,features:{testMode:!1,warmupPeriodMs:500,qualityThreshold:.6,sandboxMode:(d=this.lastSandboxMode)!=null?d:!1},handoffToken:this.temporaryHandoffToken||void 0,sessionToken:this.lastSessionToken||void 0,verifyUrl:this.lastVerifyUrl||void 0},this.config.environment,this.getHmacSecret(),this.brandConstants.name);if(this.lastVerifyUrl)try{let c=this.applyLocalVerifyOverride(this.lastVerifyUrl),p=new URL(c);return p.searchParams.set("state",a),p.searchParams.set("mode",this.config.mode),e.skipIntro&&p.searchParams.set("skip_intro","true"),e.autoReturn&&p.searchParams.set("auto_return","true"),p.toString()}catch(c){}let l=new URLSearchParams({state:a,sessionId:t,mode:this.config.mode});return e.skipIntro&&l.set("skip_intro","true"),e.autoReturn&&l.set("auto_return","true"),`${r}/?${l.toString()}`}redirect(e){window.location.href=e}openNewTab(e,t){var l,d;if(this.cleanup(),this.hasReceivedResult=!1,this.popupMonitorInterval&&(clearInterval(this.popupMonitorInterval),this.popupMonitorInterval=null),(this.config.newTabTarget||"popup")==="tab"?this.popupWindow=window.open(e,"_blank"):this.popupWindow=window.open(e,this.brandConstants.popupName,"width=600,height=700"),!this.popupWindow){(d=(l=this.config).onError)==null||d.call(l,new Error("Failed to open verification window. Please check popup blocker settings."));return}let i=this.getTrustedOrigins(),s=this.getAllowedCustomOrigins(e),o=this.brandConstants.messageType,a=this.brandConstants.legacyMessageType;this.messageListener=c=>{var D,M,_,O,$,N,K;let p=(D=c.data)==null?void 0:D.type;if(!p||typeof p!="string"||!(a?[o,a]:[o]).includes(p))return;if(!G(c,i,s,this.brandConstants.name)){g("POSTMESSAGE_ORIGIN_BLOCKED",{origin:c.origin,environment:this.config.environment,expectedOrigins:`${this.brandConstants.name} trusted origins for ${this.config.environment}`,messageType:(M=c.data)==null?void 0:M.type},this.brandConstants.name);return}let I=J(c,t,o,a);if(!I.isValid){g("POSTMESSAGE_VALIDATION_FAILED",{error:I.error,origin:c.origin,sessionId:t.substring(0,8)+"...",messageType:(_=c.data)==null?void 0:_.type},this.brandConstants.name);return}let h=c.data.status;if(h==="cancelled"){this.handleCancellation(t,"postmessage");return}let w={sessionId:c.data.sessionId,status:h,timestamp:c.data.timestamp,externalUserId:c.data.externalUserId};this.hasReceivedResult=!0,g("VERIFICATION_COMPLETED",{status:w.status,sessionId:t.substring(0,8)+"...",origin:c.origin},this.brandConstants.name),this.cleanup({closePopup:!1}),this.unlockVerification(),this.popupMonitorInterval&&(clearInterval(this.popupMonitorInterval),this.popupMonitorInterval=null),w.status==="verified"?($=(O=this.config).onComplete)==null||$.call(O,w):(K=(N=this.config).onError)==null||K.call(N,new Error(`Verification failed: ${w.status}`))},window.addEventListener("message",this.messageListener),this.popupMonitorInterval=setInterval(()=>{this.popupWindow&&this.popupWindow.closed&&(g("POPUP_CLOSED_BY_USER",{sessionId:t.substring(0,8)+"...",environment:this.config.environment},this.brandConstants.name),this.hasReceivedResult||this.handleCancellation(t,"popup-closed"))},500)}setupAutoCleanup(){if(this.unloadListener=()=>{g("SDK_AUTO_CLEANUP",{environment:this.config.environment,trigger:"page_unload"},this.brandConstants.name),this.cleanup(),this.unlockVerification()},window.addEventListener("beforeunload",this.unloadListener),window.addEventListener("pagehide",this.unloadListener),window.history&&window.history.pushState){let e=window.history.pushState;window.history.pushState=(...t)=>(this.cleanup(),this.unlockVerification(),e.apply(window.history,t))}}detectEnvironment(){let e=window.location.hostname;return e.includes("staging")||e.includes("stage")?"staging":"production"}getEnvironment(){return this.config.environment}unlockVerification(){this.isVerificationInProgress=!1,this.currentSessionId=null,g("VERIFICATION_UNLOCKED",{environment:this.config.environment,origin:window.location.origin},this.brandConstants.name)}cleanup(e={}){let t=e.closePopup!==!1;this.popupWindow&&(t&&!this.popupWindow.closed&&this.popupWindow.close(),(t||this.popupWindow.closed)&&(this.popupWindow=null)),this.messageListener&&(window.removeEventListener("message",this.messageListener),this.messageListener=null),this.popupMonitorInterval&&(clearInterval(this.popupMonitorInterval),this.popupMonitorInterval=null)}handleCancellation(e,t){if(this.hasReceivedResult)return;this.hasReceivedResult=!0,g("VERIFICATION_CANCELLED",{source:t,sessionId:e.substring(0,8)+"...",environment:this.config.environment,origin:window.location.origin},this.brandConstants.name),this.cleanup(),this.unlockVerification();let r=!0;if(this.config.onCancel)try{this.config.onCancel()===!1&&(r=!1)}catch(i){g("CANCEL_CALLBACK_FAILED",{error:i instanceof Error?i.message:String(i),sessionId:e.substring(0,8)+"..."},this.brandConstants.name)}r&&this.redirectToCancelUrl(e)}redirectToCancelUrl(e){if(this.config.cancelUrl)try{let t=decodeURIComponent(this.config.cancelUrl),r=new URL(t);r.searchParams.set("sessionId",e),r.searchParams.set("status","cancelled"),r.searchParams.set("timestamp",Date.now().toString()),this.lastExternalUserId&&r.searchParams.set("externalUserId",this.lastExternalUserId),window.location.href=r.toString()}catch(t){g("CANCEL_REDIRECT_FAILED",{error:t instanceof Error?t.message:String(t),sessionId:e.substring(0,8)+"..."},this.brandConstants.name)}}removeAutoCleanupListeners(){this.unloadListener&&(window.removeEventListener("beforeunload",this.unloadListener),window.removeEventListener("pagehide",this.unloadListener),this.unloadListener=null)}destroy(){g("SDK_DESTROYED",{environment:this.config.environment,origin:window.location.origin},this.brandConstants.name),this.cleanup(),this.unlockVerification(),this.removeAutoCleanupListeners()}getPortalApiUrl(){return this.config.apiUrl?this.config.apiUrl:this.getUrlConfig().apiUrl}getEngineUrl(){return this.getUrlConfig().engineUrl}getWebSocketUrl(){return this.getUrlConfig().wsUrl}isPublicKey(){return this.config.apiKey.startsWith("pk_")}async createInternalSession(e){var t,r;try{let i=this.getPortalApiUrl(),s=await fetch(`${i}/api/v1/sessions/create`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.config.apiKey}`},body:JSON.stringify({merchantId:this.config.apiKey,returnUrl:this.config.returnUrl,cancelUrl:this.config.cancelUrl,challengeAge:e.challengeAge,verificationMode:e.verificationMode,merchantName:document.title||window.location.hostname,externalUserId:e.externalUserId})});if(!s.ok){let l=await s.json().catch(()=>({})),d=l==null?void 0:l.code;throw this.isBillingBlockError(d)&&this.openBillingBlockPage(d,l==null?void 0:l.portalUrl),new Error(`Failed to create session: ${s.status} ${s.statusText}. ${l.message||""}`)}let o=await s.json(),a=o.sessionId;if(!a)throw new Error("Server did not return a sessionId");return o.verifyUrl&&(this.lastVerifyUrl=o.verifyUrl),o.sessionToken&&(this.lastSessionToken=o.sessionToken),o.handoffToken&&(this.temporaryHandoffToken=o.handoffToken),typeof o.sandboxMode=="boolean"?this.lastSandboxMode=o.sandboxMode:this.lastSandboxMode=null,g("INTERNAL_SESSION_CREATED",{sessionId:a.substring(0,8)+"...",environment:this.config.environment,apiKeyType:"public"},this.brandConstants.name),a}catch(i){let s=i instanceof Error?i.message:String(i);throw g("INTERNAL_SESSION_FAILED",{error:s,environment:this.config.environment,apiKeyType:"public"},this.brandConstants.name),(r=(t=this.config).onError)==null||r.call(t,i),new Error(`Failed to create verification session: ${s}`)}}isBillingBlockError(e){return e==="SUBSCRIPTION_REQUIRED"||e==="PLAN_LIMIT_REACHED"||e==="SANDBOX_LIMIT_REACHED"}openBillingBlockPage(e,t){var r,i,s,o;try{let a=this.config.verifyUrl||E(this.config.environment,this.getUrlConfig()),l=this.applyLocalVerifyOverride(a),d=new URL(l);if(d.searchParams.set("blocked",e),t&&d.searchParams.set("portalUrl",t),this.config.mode==="new-tab"){((this.config.newTabTarget||"popup")==="tab"?window.open(d.toString(),"_blank"):window.open(d.toString(),this.brandConstants.popupName,"width=600,height=700"))||(i=(r=this.config).onError)==null||i.call(r,new Error("Failed to open billing notice window. Please check popup blocker settings."));return}this.redirect(d.toString())}catch(a){let l=a instanceof Error?a.message:String(a);(o=(s=this.config).onError)==null||o.call(s,new Error(`Failed to open billing notice: ${l}`))}}getUrlConfig(){return this.brandUrls[this.config.environment]||this.brandUrls.production}getTrustedOrigins(){return this.getUrlConfig().trustedOrigins}getAllowedCustomOrigins(e){let t=new Set,r=this.getLocalOrigin(this.config.verifyUrl||null),i=this.getLocalOrigin(e||null);return r&&t.add(r),i&&t.add(i),Array.from(t)}getLocalOrigin(e){if(!e)return null;try{let t=new URL(e);if(C.LOCAL_HOSTNAMES.has(t.hostname))return t.origin}catch(t){return null}return null}applyLocalVerifyOverride(e){let t=this.getLocalOrigin(this.config.verifyUrl||null);if(!t)return e;try{let r=new URL(t),i=new URL(e);return i.protocol=r.protocol,i.host=r.host,i.toString()}catch(r){return e}}getHmacSecret(){return this.config.environment==="staging"?this.brandConstants.hmacSecretStaging:this.brandConstants.hmacSecretProd}};C.LOCAL_HOSTNAMES=new Set(["localhost","127.0.0.1","::1"]);var b=C;var le={production:{apiUrl:"https://api.safepassage.live",verifyUiUrl:"https://av.safepassage.live",engineUrl:"https://engine.safepassage.live",wsUrl:"wss://engine.safepassage.live/api/websocket/stream",trustedOrigins:["https://av.safepassage.live","https://portal.safepassage.live","https://api.safepassage.live"]},staging:{apiUrl:"https://api.staging.safepassage.live",verifyUiUrl:"https://av.staging.safepassage.live",engineUrl:"https://engine.staging.safepassage.live",wsUrl:"wss://engine.staging.safepassage.live/api/websocket/stream",trustedOrigins:["https://av.staging.safepassage.live","https://portal.staging.safepassage.live","https://api.staging.safepassage.live"]}},k={name:"SafePassage",hmacSecretProd:"safepassage-prod-hmac-2025",hmacSecretStaging:"safepassage-stage-hmac-2025",messageType:"safepassage:verification:complete",legacyMessageType:"safepassage-verification",popupName:"safepassage-verify",docsUrl:"https://docs.safepassage.live"};var m=class extends b{constructor(e){super(e,le,k)}},ce="3.4.9";m.VERSION=ce;typeof window!="undefined"&&(q(),j(`${k.name} SDK`));var be=m;return he(Ce);})();
|
|
1
|
+
/* SafePassage SDK v3.4.12 */
|
|
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);})();
|
|
3
3
|
if(typeof SafePassageSDK !== "undefined" && SafePassageSDK.SafePassage) { window.SafePassage = SafePassageSDK.SafePassage; window.SafePassage.VERSION = SafePassageSDK.VERSION; }
|
package/types/base.d.ts
CHANGED
|
@@ -156,9 +156,19 @@ export interface SessionValidationResponse {
|
|
|
156
156
|
expiresAt: string;
|
|
157
157
|
}
|
|
158
158
|
export interface SessionCreationResponse {
|
|
159
|
+
sessionId?: string;
|
|
159
160
|
sessionToken: string;
|
|
160
161
|
verifyUrl: string;
|
|
161
162
|
expiresAt: string;
|
|
163
|
+
testMode?: boolean;
|
|
164
|
+
sandboxMode?: boolean;
|
|
165
|
+
externalUserId?: string;
|
|
166
|
+
handoffToken?: string;
|
|
167
|
+
billingBlock?: {
|
|
168
|
+
code: 'SUBSCRIPTION_REQUIRED' | 'PLAN_LIMIT_REACHED' | 'SANDBOX_LIMIT_REACHED';
|
|
169
|
+
message: string;
|
|
170
|
+
portalUrl?: string;
|
|
171
|
+
};
|
|
162
172
|
}
|
|
163
173
|
export interface CreateSessionRequest {
|
|
164
174
|
returnUrl: string;
|