@privateav/sdk 3.4.2

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/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@privateav/sdk",
3
+ "version": "3.4.2",
4
+ "description": "PrivateAV SDK - Lightweight redirect-based age verification",
5
+ "main": "index.js",
6
+ "types": "index.d.ts",
7
+ "files": [
8
+ "index.js",
9
+ "index.d.ts",
10
+ "brands",
11
+ "core",
12
+ "types",
13
+ "utils",
14
+ "sdk.min.js",
15
+ "privateav.min.js",
16
+ "README.md",
17
+ "LICENSE"
18
+ ],
19
+ "keywords": [
20
+ "privateav",
21
+ "age-verification",
22
+ "redirect",
23
+ "sdk",
24
+ "typescript",
25
+ "lightweight"
26
+ ],
27
+ "author": "PrivateAV",
28
+ "license": "MIT",
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "https://github.com/privateav/sdk"
32
+ },
33
+ "bugs": {
34
+ "url": "https://github.com/privateav/sdk/issues"
35
+ },
36
+ "homepage": "https://docs.privateav.com/sdk",
37
+ "publishConfig": {
38
+ "access": "public",
39
+ "registry": "https://registry.npmjs.org/"
40
+ },
41
+ "engines": {
42
+ "node": ">=14.0.0"
43
+ }
44
+ }
@@ -0,0 +1,3 @@
1
+ /* PrivateAV SDK v3.4.2 */
2
+ "use strict";var PrivateAVSDK=(()=>{var v=Object.defineProperty,rt=Object.defineProperties,it=Object.getOwnPropertyDescriptor,ot=Object.getOwnPropertyDescriptors,st=Object.getOwnPropertyNames,w=Object.getOwnPropertySymbols;var I=Object.prototype.hasOwnProperty,M=Object.prototype.propertyIsEnumerable;var R=(e,t,n)=>t in e?v(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,u=(e,t)=>{for(var n in t||(t={}))I.call(t,n)&&R(e,n,t[n]);if(w)for(var n of w(t))M.call(t,n)&&R(e,n,t[n]);return e},y=(e,t)=>rt(e,ot(t));var _=(e,t)=>{var n={};for(var r in e)I.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(e!=null&&w)for(var r of w(e))t.indexOf(r)<0&&M.call(e,r)&&(n[r]=e[r]);return n};var C=(e,t)=>()=>(e&&(t=e(e=0)),t);var K=(e,t)=>{for(var n in t)v(e,n,{get:t[n],enumerable:!0})},at=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of st(t))!I.call(e,i)&&i!==n&&v(e,i,{get:()=>t[i],enumerable:!(r=it(t,i))||r.enumerable});return e};var ct=e=>at(v({},"__esModule",{value:!0}),e);function lt(e,t){return t.includes(e)}function O(e,t,n=[],r="SDK"){var o;let{origin:i}=e;return lt(i,t)||n.length>0&&n.some(c=>{if(c.startsWith("*.")){let s=c.slice(2);return i.endsWith(`.${s}`)||i===`https://${s}`||i===`http://${s}`}return i===c})?!0:(console.warn(`${r} Security: Blocked PostMessage from untrusted origin: ${i}`,{trustedOrigins:t,allowedCustomOrigins:n,eventType:(o=e.data)==null?void 0:o.type}),!1)}function W(e,t,n,r){let{data:i}=e;return!i||typeof i!="object"?{isValid:!1,error:"Invalid message format"}:(r?[n,r]:[n]).includes(i.type)?!i.sessionId||i.sessionId!==t?{isValid:!1,error:"Session ID mismatch"}:!i.status||!["verified","failed"].includes(i.status)?{isValid:!1,error:"Invalid status value"}:{isValid:!0}:{isValid:!1,error:"Invalid message type"}}function B(e,t="SDK"){e==="production"&&window.location.protocol!=="https:"&&console.warn(`${t} Warning: HTTPS recommended for production environment`,{current:window.location.href})}function A(e,t,n="SDK"){try{let r=new URL(e);if(r.protocol!=="https:"&&!(r.hostname==="localhost"||r.hostname==="127.0.0.1"))return{isValid:!1,error:`HTTPS required for return URLs in ${t}`};let i=[/data:/i,/javascript:/i,/vbscript:/i,/file:/i,/ftp:/i];for(let o of i)if(o.test(e))return{isValid:!1,error:"Blocked suspicious URL scheme"};return{isValid:!0}}catch(r){return{isValid:!1,error:"Invalid URL format"}}}function d(e,t,n="SDK"){let r={timestamp:new Date().toISOString(),userAgent:typeof navigator!="undefined"?navigator.userAgent:"unknown",url:typeof window!="undefined"?window.location.href:"unknown"};console.warn(`${n} Security Event: ${e}`,u(u({},r),t))}var b,H,T=C(()=>{"use strict";b=class{constructor(){this.attempts=new Map;this.maxAttempts=5;this.timeWindow=6e4}isAllowed(t,n="SDK"){let r=Date.now(),o=(this.attempts.get(t)||[]).filter(a=>r-a<this.timeWindow);return o.length>=this.maxAttempts?(console.warn(`${n} Security: Rate limit exceeded for ${t}`),!1):(o.push(r),this.attempts.set(t,o),!0)}reset(t){this.attempts.delete(t)}},H=new b});var F={};K(F,{createSignedState:()=>pt,generateHMAC:()=>P,generateSecureToken:()=>j,parseSignedState:()=>ut,verifyHMAC:()=>q});async function P(e,t){let n=new TextEncoder,r=n.encode(t),i=n.encode(e),o=await crypto.subtle.importKey("raw",r,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),a=await crypto.subtle.sign("HMAC",o,i);return Array.from(new Uint8Array(a)).map(c=>c.toString(16).padStart(2,"0")).join("")}async function q(e,t,n){try{let r=await P(e,n);return dt(t,r)}catch(r){return!1}}function dt(e,t){if(e.length!==t.length)return!1;let n=0;for(let r=0;r<e.length;r++)n|=e.charCodeAt(r)^t.charCodeAt(r);return n===0}function j(e=32){let t=new Uint8Array(e);return crypto.getRandomValues(t),Array.from(t,n=>n.toString(16).padStart(2,"0")).join("")}async function pt(e,t){let n=y(u({},e),{timestamp:Date.now(),nonce:j(16)}),r=JSON.stringify(n),i=await P(r,t);return btoa(JSON.stringify({data:n,signature:i}))}async function ut(e,t,n=J,r="SDK"){try{let o=atob(e),a=JSON.parse(o);if(!a.data||!a.signature)return console.warn(`${r}: Invalid signed state format`),null;let{data:c,signature:s}=a,l=JSON.stringify(c);if(!await q(l,s,t))return console.warn(`${r}: State signature verification failed`),null;if(c.timestamp){let h=Date.now()-c.timestamp;if(h>n)return console.warn(`${r}: State parameter expired`,{age:h,maxAge:n}),null}let i=c,{timestamp:g,nonce:f}=i;return _(i,["timestamp","nonce"])}catch(o){return console.warn(`${r}: Failed to parse signed state`,o),null}}var G=C(()=>{"use strict";x()});function Z(e,t){if(!e.apiKey)throw new Error("apiKey is required");if(e.apiKey.length>z)throw new Error(`apiKey exceeds maximum length of ${z} characters`);if(!gt.test(e.apiKey))throw new Error("Invalid apiKey format. Expected pk_xxx (public) or sk_xxx (private)");if(typeof window!="undefined"&&e.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: ${t.docsUrl}/server-side-sessions`);if(!e.returnUrl)throw new Error("returnUrl is required");if(e.returnUrl.length>S)throw new Error(`returnUrl exceeds maximum length of ${S} characters`);let n=ft(),r=A(e.returnUrl,n,t.brandName);if(!r.isValid)throw new Error(`returnUrl validation failed: ${r.error}`);if(e.cancelUrl){if(e.cancelUrl.length>S)throw new Error(`cancelUrl exceeds maximum length of ${S} characters`);let i=A(e.cancelUrl,n,t.brandName);if(!i.isValid)throw new Error(`cancelUrl validation failed: ${i.error}`)}if(e.defaultChallengeAge!==void 0){if(e.defaultChallengeAge<X)throw new Error(`defaultChallengeAge must be at least ${X}`);if(e.defaultChallengeAge>Y)throw new Error(`defaultChallengeAge cannot exceed ${Y}`)}if(e.defaultVerificationMode&&!["L1","L2"].includes(e.defaultVerificationMode))throw new Error("defaultVerificationMode must be L1 or L2");if(e.mode&&!["redirect","new-tab"].includes(e.mode))throw new Error("mode must be redirect or new-tab")}function ft(){if(typeof window=="undefined")return"production";let e=window.location.hostname;return e.includes("staging")||e.includes("stage")?"staging":"production"}async function Q(e,t,n,r="SDK"){let{createSignedState:i}=await Promise.resolve().then(()=>(G(),F));return i(e,n)}var X,Y,S,z,J,gt,x=C(()=>{"use strict";T();X=25,Y=150,S=2048,z=128,J=6e5,gt=/^(pk_|sk_)[a-zA-Z0-9_]+$/});var wt={};K(wt,{PrivateAV:()=>m,VERSION:()=>nt,default:()=>mt});function N(){crypto.randomUUID||(crypto.randomUUID=function(){let e=new Uint8Array(16);crypto.getRandomValues(e),e[6]=e[6]&15|64,e[8]=e[8]&63|128;let t=Array.from(e).map(n=>n.toString(16).padStart(2,"0")).join("");return[t.slice(0,8),t.slice(8,12),t.slice(12,16),t.slice(16,20),t.slice(20,32)].join("-")})}function L(e="SDK"){let t=[];if(!window.crypto||!window.crypto.getRandomValues)throw new Error(`${e} requires Web Crypto API support`);if(!window.crypto.subtle)throw new Error(`${e} requires Web Crypto subtle API for HMAC operations`);crypto.randomUUID||t.push("crypto.randomUUID not supported, using polyfill"),window.URLSearchParams||t.push("URLSearchParams not supported, consider adding a polyfill for IE 11 support"),t.length>0&&console.warn(`${e} Browser Compatibility:`,t.join("; "))}x();function V(e,t){let n=t.verifyUiUrl;if(!n||!n.startsWith("https://"))throw new Error(`HTTPS required for ${e} environment`);return n}function ht(e,t){let n=t.apiUrl;if(!n||!n.startsWith("https://"))throw new Error(`HTTPS required for API URLs in ${e} environment`);return n}function tt(e,t,n="SDK"){let r=window.location.protocol==="https:";switch(e){case"production":r||console.warn(`${n} Warning: HTTPS recommended for production environment`);break;case"staging":r||console.warn(`${n} Warning: HTTPS strongly recommended in staging environment`);break}try{V(e,t),ht(e,t)}catch(i){let o=i instanceof Error?i.message:String(i);throw new Error(`Environment configuration validation failed: ${o}`)}}T();var U=class{constructor(t,n,r){this.popupWindow=null;this.messageListener=null;this.popupMonitorInterval=null;this.unloadListener=null;this.isVerificationInProgress=!1;this.currentSessionId=null;this.lastVerifyUrl=null;this.lastSessionToken=null;this.temporaryHandoffToken=null;this.brandUrls=n,this.brandConstants=r,Z(t,{brandName:this.brandConstants.name,docsUrl:this.brandConstants.docsUrl});let i=t.environment||this.detectEnvironment();i!=="staging"&&i!=="production"&&(console.warn(`${this.brandConstants.name} SDK: Unknown environment '${i}', defaulting to 'production'`),i="production"),this.config=y(u({},t),{environment:i,mode:t.mode||"redirect"}),tt(this.config.environment,this.getUrlConfig(),this.brandConstants.name),B(this.config.environment,this.brandConstants.name),d("SDK_INITIALIZED",{environment:this.config.environment,mode:this.config.mode,origin:window.location.origin,protocol:window.location.protocol,hostname:window.location.hostname},this.brandConstants.name),this.setupAutoCleanup()}async verify(t={}){var i,o,a,c,s,l;let n=this.isPublicKey(),r;if(n)r=await this.createInternalSession(t);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 p=new Error(`Verification already in progress for session ${(i=this.currentSessionId)==null?void 0:i.substring(0,8)}...`);throw d("RACE_CONDITION_PREVENTED",{currentSession:((o=this.currentSessionId)==null?void 0:o.substring(0,8))+"...",attemptedSession:"new-session-attempt",origin:window.location.origin},this.brandConstants.name),(c=(a=this.config).onError)==null||c.call(a,p),p}this.isVerificationInProgress=!0,this.currentSessionId=r;try{let p=`${this.config.apiKey}:${window.location.origin}`;if(!H.isAllowed(p,this.brandConstants.name)){let f=new Error("Too many verification attempts. Please wait before trying again.");throw d("RATE_LIMIT_EXCEEDED",{apiKey:this.config.apiKey.substring(0,8)+"...",origin:window.location.origin,sessionId:r?r.substring(0,8)+"...":"undefined"},this.brandConstants.name),(l=(s=this.config).onError)==null||l.call(s,f),f}let g=await this.buildVerificationUrl(t,r);d("VERIFICATION_INITIATED",{environment:this.config.environment,mode:this.config.mode,sessionId:r?r.substring(0,8)+"...":"undefined",origin:window.location.origin},this.brandConstants.name),this.config.mode==="new-tab"?this.openNewTab(g,r):(this.unlockVerification(),this.redirect(g))}catch(p){throw this.unlockVerification(),p}}async buildVerificationUrl(t,n){let r=this.config.verifyUrl||V(this.config.environment,this.getUrlConfig()),i=t.challengeAge!==void 0,o=t.verificationMode!==void 0,a=i||o,c=await Q({merchantId:this.config.apiKey,sessionId:n,returnUrl:this.config.returnUrl,cancelUrl:this.config.cancelUrl,challengeAge:t.challengeAge||this.config.defaultChallengeAge,verificationMode:t.verificationMode||this.config.defaultVerificationMode,hasOverrides:a,externalUserId:t.externalUserId,timestamp:Date.now(),apiUrl:this.getPortalApiUrl(),engineUrl:this.getEngineUrl(),wsUrl:this.getWebSocketUrl(),environment:this.config.environment,features:{testMode:!1,warmupPeriodMs:500,qualityThreshold:.6},handoffToken:this.temporaryHandoffToken||void 0,sessionToken:this.lastSessionToken||void 0,verifyUrl:this.lastVerifyUrl||void 0},this.config.environment,this.getHmacSecret(),this.brandConstants.name);if(this.lastVerifyUrl)try{let l=new URL(this.lastVerifyUrl);return l.searchParams.set("state",c),l.searchParams.set("mode",this.config.mode),t.skipIntro&&l.searchParams.set("skip_intro","true"),t.autoReturn&&l.searchParams.set("auto_return","true"),l.toString()}catch(l){}let s=new URLSearchParams({state:c,sessionId:n,mode:this.config.mode});return t.skipIntro&&s.set("skip_intro","true"),t.autoReturn&&s.set("auto_return","true"),`${r}/?${s.toString()}`}redirect(t){window.location.href=t}openNewTab(t,n){var a,c;if(this.cleanup(),this.popupMonitorInterval&&(clearInterval(this.popupMonitorInterval),this.popupMonitorInterval=null),this.popupWindow=window.open(t,this.brandConstants.popupName,"width=600,height=700"),!this.popupWindow){(c=(a=this.config).onError)==null||c.call(a,new Error("Failed to open verification window. Please check popup blocker settings."));return}let r=this.getTrustedOrigins(),i=this.brandConstants.messageType,o=this.brandConstants.legacyMessageType;this.messageListener=s=>{var g,f,E,h,D,$;if(!O(s,r,[],this.brandConstants.name)){d("POSTMESSAGE_ORIGIN_BLOCKED",{origin:s.origin,environment:this.config.environment,expectedOrigins:`${this.brandConstants.name} trusted origins for ${this.config.environment}`,messageType:(g=s.data)==null?void 0:g.type},this.brandConstants.name);return}let l=W(s,n,i,o);if(!l.isValid){d("POSTMESSAGE_VALIDATION_FAILED",{error:l.error,origin:s.origin,sessionId:n.substring(0,8)+"...",messageType:(f=s.data)==null?void 0:f.type},this.brandConstants.name);return}let p={sessionId:s.data.sessionId,status:s.data.status};d("VERIFICATION_COMPLETED",{status:p.status,sessionId:n.substring(0,8)+"...",origin:s.origin},this.brandConstants.name),this.cleanup(),this.unlockVerification(),this.popupMonitorInterval&&(clearInterval(this.popupMonitorInterval),this.popupMonitorInterval=null),p.status==="verified"?(h=(E=this.config).onComplete)==null||h.call(E,p):($=(D=this.config).onError)==null||$.call(D,new Error(`Verification failed: ${p.status}`))},window.addEventListener("message",this.messageListener),this.popupMonitorInterval=setInterval(()=>{var s,l;this.popupWindow&&this.popupWindow.closed&&(d("POPUP_CLOSED_BY_USER",{sessionId:n.substring(0,8)+"...",environment:this.config.environment},this.brandConstants.name),this.cleanup(),this.unlockVerification(),(l=(s=this.config).onCancel)==null||l.call(s))},500)}setupAutoCleanup(){if(this.unloadListener=()=>{d("SDK_AUTO_CLEANUP",{environment:this.config.environment,trigger:"page_unload"},this.brandConstants.name),this.cleanup(),this.unlockVerification()},window.addEventListener("beforeunload",this.unloadListener),window.addEventListener("pagehide",this.unloadListener),window.history&&window.history.pushState){let t=window.history.pushState;window.history.pushState=(...n)=>(this.cleanup(),this.unlockVerification(),t.apply(window.history,n))}}detectEnvironment(){let t=window.location.hostname;return t.includes("staging")||t.includes("stage")?"staging":"production"}getEnvironment(){return this.config.environment}unlockVerification(){this.isVerificationInProgress=!1,this.currentSessionId=null,d("VERIFICATION_UNLOCKED",{environment:this.config.environment,origin:window.location.origin},this.brandConstants.name)}cleanup(){this.popupWindow&&!this.popupWindow.closed&&this.popupWindow.close(),this.popupWindow=null,this.messageListener&&(window.removeEventListener("message",this.messageListener),this.messageListener=null),this.popupMonitorInterval&&(clearInterval(this.popupMonitorInterval),this.popupMonitorInterval=null)}removeAutoCleanupListeners(){this.unloadListener&&(window.removeEventListener("beforeunload",this.unloadListener),window.removeEventListener("pagehide",this.unloadListener),this.unloadListener=null)}destroy(){d("SDK_DESTROYED",{environment:this.config.environment,origin:window.location.origin},this.brandConstants.name),this.cleanup(),this.unlockVerification(),this.removeAutoCleanupListeners()}getPortalApiUrl(){return this.config.apiUrl?this.config.apiUrl:this.getUrlConfig().apiUrl}getEngineUrl(){return this.getUrlConfig().engineUrl}getWebSocketUrl(){return this.getUrlConfig().wsUrl}isPublicKey(){return this.config.apiKey.startsWith("pk_")}async createInternalSession(t){var n,r;try{let i=this.getPortalApiUrl(),o=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:t.challengeAge,verificationMode:t.verificationMode,merchantName:document.title||window.location.hostname,externalUserId:t.externalUserId})});if(!o.ok){let s=await o.json().catch(()=>({}));throw new Error(`Failed to create session: ${o.status} ${o.statusText}. ${s.message||""}`)}let a=await o.json(),c=a.sessionId;if(!c)throw new Error("Server did not return a sessionId");return a.verifyUrl&&(this.lastVerifyUrl=a.verifyUrl),a.sessionToken&&(this.lastSessionToken=a.sessionToken),a.handoffToken&&(this.temporaryHandoffToken=a.handoffToken),d("INTERNAL_SESSION_CREATED",{sessionId:c.substring(0,8)+"...",environment:this.config.environment,apiKeyType:"public"},this.brandConstants.name),c}catch(i){let o=i instanceof Error?i.message:String(i);throw d("INTERNAL_SESSION_FAILED",{error:o,environment:this.config.environment,apiKeyType:"public"},this.brandConstants.name),(r=(n=this.config).onError)==null||r.call(n,i),new Error(`Failed to create verification session: ${o}`)}}getUrlConfig(){return this.brandUrls[this.config.environment]||this.brandUrls.production}getTrustedOrigins(){return this.getUrlConfig().trustedOrigins}getHmacSecret(){return this.config.environment==="staging"?this.brandConstants.hmacSecretStaging:this.brandConstants.hmacSecretProd}};var et={production:{apiUrl:"https://api.privateav.com",verifyUiUrl:"https://verify.privateav.com",engineUrl:"https://engine.privateav.com",wsUrl:"wss://engine.privateav.com/api/websocket/stream",trustedOrigins:["https://verify.privateav.com","https://portal.privateav.com","https://api.privateav.com"]},staging:{apiUrl:"https://api.staging.privateav.com",verifyUiUrl:"https://verify.staging.privateav.com",engineUrl:"https://engine.staging.privateav.com",wsUrl:"wss://engine.staging.privateav.com/api/websocket/stream",trustedOrigins:["https://verify.staging.privateav.com","https://portal.staging.privateav.com","https://api.staging.privateav.com"]}},k={name:"PrivateAV",hmacSecretProd:"privateav-prod-hmac-2025",hmacSecretStaging:"privateav-stage-hmac-2025",messageType:"privateav:verification:complete",legacyMessageType:"privateav-verification",popupName:"privateav-verify",docsUrl:"https://docs.privateav.com"};var m=class extends U{constructor(t){super(t,et,k)}},nt="3.4.2";m.VERSION=nt;typeof window!="undefined"&&(N(),L(`${k.name} SDK`));var mt=m;return ct(wt);})();
3
+ if(typeof PrivateAVSDK !== "undefined" && PrivateAVSDK.PrivateAV) { window.PrivateAV = PrivateAVSDK.PrivateAV; window.PrivateAV.VERSION = PrivateAVSDK.VERSION; }
package/sdk.min.js ADDED
@@ -0,0 +1,3 @@
1
+ /* PrivateAV SDK v3.4.2 */
2
+ "use strict";var PrivateAVSDK=(()=>{var v=Object.defineProperty,rt=Object.defineProperties,it=Object.getOwnPropertyDescriptor,ot=Object.getOwnPropertyDescriptors,st=Object.getOwnPropertyNames,w=Object.getOwnPropertySymbols;var I=Object.prototype.hasOwnProperty,M=Object.prototype.propertyIsEnumerable;var R=(e,t,n)=>t in e?v(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,u=(e,t)=>{for(var n in t||(t={}))I.call(t,n)&&R(e,n,t[n]);if(w)for(var n of w(t))M.call(t,n)&&R(e,n,t[n]);return e},y=(e,t)=>rt(e,ot(t));var _=(e,t)=>{var n={};for(var r in e)I.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(e!=null&&w)for(var r of w(e))t.indexOf(r)<0&&M.call(e,r)&&(n[r]=e[r]);return n};var C=(e,t)=>()=>(e&&(t=e(e=0)),t);var K=(e,t)=>{for(var n in t)v(e,n,{get:t[n],enumerable:!0})},at=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of st(t))!I.call(e,i)&&i!==n&&v(e,i,{get:()=>t[i],enumerable:!(r=it(t,i))||r.enumerable});return e};var ct=e=>at(v({},"__esModule",{value:!0}),e);function lt(e,t){return t.includes(e)}function O(e,t,n=[],r="SDK"){var o;let{origin:i}=e;return lt(i,t)||n.length>0&&n.some(c=>{if(c.startsWith("*.")){let s=c.slice(2);return i.endsWith(`.${s}`)||i===`https://${s}`||i===`http://${s}`}return i===c})?!0:(console.warn(`${r} Security: Blocked PostMessage from untrusted origin: ${i}`,{trustedOrigins:t,allowedCustomOrigins:n,eventType:(o=e.data)==null?void 0:o.type}),!1)}function W(e,t,n,r){let{data:i}=e;return!i||typeof i!="object"?{isValid:!1,error:"Invalid message format"}:(r?[n,r]:[n]).includes(i.type)?!i.sessionId||i.sessionId!==t?{isValid:!1,error:"Session ID mismatch"}:!i.status||!["verified","failed"].includes(i.status)?{isValid:!1,error:"Invalid status value"}:{isValid:!0}:{isValid:!1,error:"Invalid message type"}}function B(e,t="SDK"){e==="production"&&window.location.protocol!=="https:"&&console.warn(`${t} Warning: HTTPS recommended for production environment`,{current:window.location.href})}function A(e,t,n="SDK"){try{let r=new URL(e);if(r.protocol!=="https:"&&!(r.hostname==="localhost"||r.hostname==="127.0.0.1"))return{isValid:!1,error:`HTTPS required for return URLs in ${t}`};let i=[/data:/i,/javascript:/i,/vbscript:/i,/file:/i,/ftp:/i];for(let o of i)if(o.test(e))return{isValid:!1,error:"Blocked suspicious URL scheme"};return{isValid:!0}}catch(r){return{isValid:!1,error:"Invalid URL format"}}}function d(e,t,n="SDK"){let r={timestamp:new Date().toISOString(),userAgent:typeof navigator!="undefined"?navigator.userAgent:"unknown",url:typeof window!="undefined"?window.location.href:"unknown"};console.warn(`${n} Security Event: ${e}`,u(u({},r),t))}var b,H,T=C(()=>{"use strict";b=class{constructor(){this.attempts=new Map;this.maxAttempts=5;this.timeWindow=6e4}isAllowed(t,n="SDK"){let r=Date.now(),o=(this.attempts.get(t)||[]).filter(a=>r-a<this.timeWindow);return o.length>=this.maxAttempts?(console.warn(`${n} Security: Rate limit exceeded for ${t}`),!1):(o.push(r),this.attempts.set(t,o),!0)}reset(t){this.attempts.delete(t)}},H=new b});var F={};K(F,{createSignedState:()=>pt,generateHMAC:()=>P,generateSecureToken:()=>j,parseSignedState:()=>ut,verifyHMAC:()=>q});async function P(e,t){let n=new TextEncoder,r=n.encode(t),i=n.encode(e),o=await crypto.subtle.importKey("raw",r,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),a=await crypto.subtle.sign("HMAC",o,i);return Array.from(new Uint8Array(a)).map(c=>c.toString(16).padStart(2,"0")).join("")}async function q(e,t,n){try{let r=await P(e,n);return dt(t,r)}catch(r){return!1}}function dt(e,t){if(e.length!==t.length)return!1;let n=0;for(let r=0;r<e.length;r++)n|=e.charCodeAt(r)^t.charCodeAt(r);return n===0}function j(e=32){let t=new Uint8Array(e);return crypto.getRandomValues(t),Array.from(t,n=>n.toString(16).padStart(2,"0")).join("")}async function pt(e,t){let n=y(u({},e),{timestamp:Date.now(),nonce:j(16)}),r=JSON.stringify(n),i=await P(r,t);return btoa(JSON.stringify({data:n,signature:i}))}async function ut(e,t,n=J,r="SDK"){try{let o=atob(e),a=JSON.parse(o);if(!a.data||!a.signature)return console.warn(`${r}: Invalid signed state format`),null;let{data:c,signature:s}=a,l=JSON.stringify(c);if(!await q(l,s,t))return console.warn(`${r}: State signature verification failed`),null;if(c.timestamp){let h=Date.now()-c.timestamp;if(h>n)return console.warn(`${r}: State parameter expired`,{age:h,maxAge:n}),null}let i=c,{timestamp:g,nonce:f}=i;return _(i,["timestamp","nonce"])}catch(o){return console.warn(`${r}: Failed to parse signed state`,o),null}}var G=C(()=>{"use strict";x()});function Z(e,t){if(!e.apiKey)throw new Error("apiKey is required");if(e.apiKey.length>z)throw new Error(`apiKey exceeds maximum length of ${z} characters`);if(!gt.test(e.apiKey))throw new Error("Invalid apiKey format. Expected pk_xxx (public) or sk_xxx (private)");if(typeof window!="undefined"&&e.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: ${t.docsUrl}/server-side-sessions`);if(!e.returnUrl)throw new Error("returnUrl is required");if(e.returnUrl.length>S)throw new Error(`returnUrl exceeds maximum length of ${S} characters`);let n=ft(),r=A(e.returnUrl,n,t.brandName);if(!r.isValid)throw new Error(`returnUrl validation failed: ${r.error}`);if(e.cancelUrl){if(e.cancelUrl.length>S)throw new Error(`cancelUrl exceeds maximum length of ${S} characters`);let i=A(e.cancelUrl,n,t.brandName);if(!i.isValid)throw new Error(`cancelUrl validation failed: ${i.error}`)}if(e.defaultChallengeAge!==void 0){if(e.defaultChallengeAge<X)throw new Error(`defaultChallengeAge must be at least ${X}`);if(e.defaultChallengeAge>Y)throw new Error(`defaultChallengeAge cannot exceed ${Y}`)}if(e.defaultVerificationMode&&!["L1","L2"].includes(e.defaultVerificationMode))throw new Error("defaultVerificationMode must be L1 or L2");if(e.mode&&!["redirect","new-tab"].includes(e.mode))throw new Error("mode must be redirect or new-tab")}function ft(){if(typeof window=="undefined")return"production";let e=window.location.hostname;return e.includes("staging")||e.includes("stage")?"staging":"production"}async function Q(e,t,n,r="SDK"){let{createSignedState:i}=await Promise.resolve().then(()=>(G(),F));return i(e,n)}var X,Y,S,z,J,gt,x=C(()=>{"use strict";T();X=25,Y=150,S=2048,z=128,J=6e5,gt=/^(pk_|sk_)[a-zA-Z0-9_]+$/});var wt={};K(wt,{PrivateAV:()=>m,VERSION:()=>nt,default:()=>mt});function N(){crypto.randomUUID||(crypto.randomUUID=function(){let e=new Uint8Array(16);crypto.getRandomValues(e),e[6]=e[6]&15|64,e[8]=e[8]&63|128;let t=Array.from(e).map(n=>n.toString(16).padStart(2,"0")).join("");return[t.slice(0,8),t.slice(8,12),t.slice(12,16),t.slice(16,20),t.slice(20,32)].join("-")})}function L(e="SDK"){let t=[];if(!window.crypto||!window.crypto.getRandomValues)throw new Error(`${e} requires Web Crypto API support`);if(!window.crypto.subtle)throw new Error(`${e} requires Web Crypto subtle API for HMAC operations`);crypto.randomUUID||t.push("crypto.randomUUID not supported, using polyfill"),window.URLSearchParams||t.push("URLSearchParams not supported, consider adding a polyfill for IE 11 support"),t.length>0&&console.warn(`${e} Browser Compatibility:`,t.join("; "))}x();function V(e,t){let n=t.verifyUiUrl;if(!n||!n.startsWith("https://"))throw new Error(`HTTPS required for ${e} environment`);return n}function ht(e,t){let n=t.apiUrl;if(!n||!n.startsWith("https://"))throw new Error(`HTTPS required for API URLs in ${e} environment`);return n}function tt(e,t,n="SDK"){let r=window.location.protocol==="https:";switch(e){case"production":r||console.warn(`${n} Warning: HTTPS recommended for production environment`);break;case"staging":r||console.warn(`${n} Warning: HTTPS strongly recommended in staging environment`);break}try{V(e,t),ht(e,t)}catch(i){let o=i instanceof Error?i.message:String(i);throw new Error(`Environment configuration validation failed: ${o}`)}}T();var U=class{constructor(t,n,r){this.popupWindow=null;this.messageListener=null;this.popupMonitorInterval=null;this.unloadListener=null;this.isVerificationInProgress=!1;this.currentSessionId=null;this.lastVerifyUrl=null;this.lastSessionToken=null;this.temporaryHandoffToken=null;this.brandUrls=n,this.brandConstants=r,Z(t,{brandName:this.brandConstants.name,docsUrl:this.brandConstants.docsUrl});let i=t.environment||this.detectEnvironment();i!=="staging"&&i!=="production"&&(console.warn(`${this.brandConstants.name} SDK: Unknown environment '${i}', defaulting to 'production'`),i="production"),this.config=y(u({},t),{environment:i,mode:t.mode||"redirect"}),tt(this.config.environment,this.getUrlConfig(),this.brandConstants.name),B(this.config.environment,this.brandConstants.name),d("SDK_INITIALIZED",{environment:this.config.environment,mode:this.config.mode,origin:window.location.origin,protocol:window.location.protocol,hostname:window.location.hostname},this.brandConstants.name),this.setupAutoCleanup()}async verify(t={}){var i,o,a,c,s,l;let n=this.isPublicKey(),r;if(n)r=await this.createInternalSession(t);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 p=new Error(`Verification already in progress for session ${(i=this.currentSessionId)==null?void 0:i.substring(0,8)}...`);throw d("RACE_CONDITION_PREVENTED",{currentSession:((o=this.currentSessionId)==null?void 0:o.substring(0,8))+"...",attemptedSession:"new-session-attempt",origin:window.location.origin},this.brandConstants.name),(c=(a=this.config).onError)==null||c.call(a,p),p}this.isVerificationInProgress=!0,this.currentSessionId=r;try{let p=`${this.config.apiKey}:${window.location.origin}`;if(!H.isAllowed(p,this.brandConstants.name)){let f=new Error("Too many verification attempts. Please wait before trying again.");throw d("RATE_LIMIT_EXCEEDED",{apiKey:this.config.apiKey.substring(0,8)+"...",origin:window.location.origin,sessionId:r?r.substring(0,8)+"...":"undefined"},this.brandConstants.name),(l=(s=this.config).onError)==null||l.call(s,f),f}let g=await this.buildVerificationUrl(t,r);d("VERIFICATION_INITIATED",{environment:this.config.environment,mode:this.config.mode,sessionId:r?r.substring(0,8)+"...":"undefined",origin:window.location.origin},this.brandConstants.name),this.config.mode==="new-tab"?this.openNewTab(g,r):(this.unlockVerification(),this.redirect(g))}catch(p){throw this.unlockVerification(),p}}async buildVerificationUrl(t,n){let r=this.config.verifyUrl||V(this.config.environment,this.getUrlConfig()),i=t.challengeAge!==void 0,o=t.verificationMode!==void 0,a=i||o,c=await Q({merchantId:this.config.apiKey,sessionId:n,returnUrl:this.config.returnUrl,cancelUrl:this.config.cancelUrl,challengeAge:t.challengeAge||this.config.defaultChallengeAge,verificationMode:t.verificationMode||this.config.defaultVerificationMode,hasOverrides:a,externalUserId:t.externalUserId,timestamp:Date.now(),apiUrl:this.getPortalApiUrl(),engineUrl:this.getEngineUrl(),wsUrl:this.getWebSocketUrl(),environment:this.config.environment,features:{testMode:!1,warmupPeriodMs:500,qualityThreshold:.6},handoffToken:this.temporaryHandoffToken||void 0,sessionToken:this.lastSessionToken||void 0,verifyUrl:this.lastVerifyUrl||void 0},this.config.environment,this.getHmacSecret(),this.brandConstants.name);if(this.lastVerifyUrl)try{let l=new URL(this.lastVerifyUrl);return l.searchParams.set("state",c),l.searchParams.set("mode",this.config.mode),t.skipIntro&&l.searchParams.set("skip_intro","true"),t.autoReturn&&l.searchParams.set("auto_return","true"),l.toString()}catch(l){}let s=new URLSearchParams({state:c,sessionId:n,mode:this.config.mode});return t.skipIntro&&s.set("skip_intro","true"),t.autoReturn&&s.set("auto_return","true"),`${r}/?${s.toString()}`}redirect(t){window.location.href=t}openNewTab(t,n){var a,c;if(this.cleanup(),this.popupMonitorInterval&&(clearInterval(this.popupMonitorInterval),this.popupMonitorInterval=null),this.popupWindow=window.open(t,this.brandConstants.popupName,"width=600,height=700"),!this.popupWindow){(c=(a=this.config).onError)==null||c.call(a,new Error("Failed to open verification window. Please check popup blocker settings."));return}let r=this.getTrustedOrigins(),i=this.brandConstants.messageType,o=this.brandConstants.legacyMessageType;this.messageListener=s=>{var g,f,E,h,D,$;if(!O(s,r,[],this.brandConstants.name)){d("POSTMESSAGE_ORIGIN_BLOCKED",{origin:s.origin,environment:this.config.environment,expectedOrigins:`${this.brandConstants.name} trusted origins for ${this.config.environment}`,messageType:(g=s.data)==null?void 0:g.type},this.brandConstants.name);return}let l=W(s,n,i,o);if(!l.isValid){d("POSTMESSAGE_VALIDATION_FAILED",{error:l.error,origin:s.origin,sessionId:n.substring(0,8)+"...",messageType:(f=s.data)==null?void 0:f.type},this.brandConstants.name);return}let p={sessionId:s.data.sessionId,status:s.data.status};d("VERIFICATION_COMPLETED",{status:p.status,sessionId:n.substring(0,8)+"...",origin:s.origin},this.brandConstants.name),this.cleanup(),this.unlockVerification(),this.popupMonitorInterval&&(clearInterval(this.popupMonitorInterval),this.popupMonitorInterval=null),p.status==="verified"?(h=(E=this.config).onComplete)==null||h.call(E,p):($=(D=this.config).onError)==null||$.call(D,new Error(`Verification failed: ${p.status}`))},window.addEventListener("message",this.messageListener),this.popupMonitorInterval=setInterval(()=>{var s,l;this.popupWindow&&this.popupWindow.closed&&(d("POPUP_CLOSED_BY_USER",{sessionId:n.substring(0,8)+"...",environment:this.config.environment},this.brandConstants.name),this.cleanup(),this.unlockVerification(),(l=(s=this.config).onCancel)==null||l.call(s))},500)}setupAutoCleanup(){if(this.unloadListener=()=>{d("SDK_AUTO_CLEANUP",{environment:this.config.environment,trigger:"page_unload"},this.brandConstants.name),this.cleanup(),this.unlockVerification()},window.addEventListener("beforeunload",this.unloadListener),window.addEventListener("pagehide",this.unloadListener),window.history&&window.history.pushState){let t=window.history.pushState;window.history.pushState=(...n)=>(this.cleanup(),this.unlockVerification(),t.apply(window.history,n))}}detectEnvironment(){let t=window.location.hostname;return t.includes("staging")||t.includes("stage")?"staging":"production"}getEnvironment(){return this.config.environment}unlockVerification(){this.isVerificationInProgress=!1,this.currentSessionId=null,d("VERIFICATION_UNLOCKED",{environment:this.config.environment,origin:window.location.origin},this.brandConstants.name)}cleanup(){this.popupWindow&&!this.popupWindow.closed&&this.popupWindow.close(),this.popupWindow=null,this.messageListener&&(window.removeEventListener("message",this.messageListener),this.messageListener=null),this.popupMonitorInterval&&(clearInterval(this.popupMonitorInterval),this.popupMonitorInterval=null)}removeAutoCleanupListeners(){this.unloadListener&&(window.removeEventListener("beforeunload",this.unloadListener),window.removeEventListener("pagehide",this.unloadListener),this.unloadListener=null)}destroy(){d("SDK_DESTROYED",{environment:this.config.environment,origin:window.location.origin},this.brandConstants.name),this.cleanup(),this.unlockVerification(),this.removeAutoCleanupListeners()}getPortalApiUrl(){return this.config.apiUrl?this.config.apiUrl:this.getUrlConfig().apiUrl}getEngineUrl(){return this.getUrlConfig().engineUrl}getWebSocketUrl(){return this.getUrlConfig().wsUrl}isPublicKey(){return this.config.apiKey.startsWith("pk_")}async createInternalSession(t){var n,r;try{let i=this.getPortalApiUrl(),o=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:t.challengeAge,verificationMode:t.verificationMode,merchantName:document.title||window.location.hostname,externalUserId:t.externalUserId})});if(!o.ok){let s=await o.json().catch(()=>({}));throw new Error(`Failed to create session: ${o.status} ${o.statusText}. ${s.message||""}`)}let a=await o.json(),c=a.sessionId;if(!c)throw new Error("Server did not return a sessionId");return a.verifyUrl&&(this.lastVerifyUrl=a.verifyUrl),a.sessionToken&&(this.lastSessionToken=a.sessionToken),a.handoffToken&&(this.temporaryHandoffToken=a.handoffToken),d("INTERNAL_SESSION_CREATED",{sessionId:c.substring(0,8)+"...",environment:this.config.environment,apiKeyType:"public"},this.brandConstants.name),c}catch(i){let o=i instanceof Error?i.message:String(i);throw d("INTERNAL_SESSION_FAILED",{error:o,environment:this.config.environment,apiKeyType:"public"},this.brandConstants.name),(r=(n=this.config).onError)==null||r.call(n,i),new Error(`Failed to create verification session: ${o}`)}}getUrlConfig(){return this.brandUrls[this.config.environment]||this.brandUrls.production}getTrustedOrigins(){return this.getUrlConfig().trustedOrigins}getHmacSecret(){return this.config.environment==="staging"?this.brandConstants.hmacSecretStaging:this.brandConstants.hmacSecretProd}};var et={production:{apiUrl:"https://api.privateav.com",verifyUiUrl:"https://verify.privateav.com",engineUrl:"https://engine.privateav.com",wsUrl:"wss://engine.privateav.com/api/websocket/stream",trustedOrigins:["https://verify.privateav.com","https://portal.privateav.com","https://api.privateav.com"]},staging:{apiUrl:"https://api.staging.privateav.com",verifyUiUrl:"https://verify.staging.privateav.com",engineUrl:"https://engine.staging.privateav.com",wsUrl:"wss://engine.staging.privateav.com/api/websocket/stream",trustedOrigins:["https://verify.staging.privateav.com","https://portal.staging.privateav.com","https://api.staging.privateav.com"]}},k={name:"PrivateAV",hmacSecretProd:"privateav-prod-hmac-2025",hmacSecretStaging:"privateav-stage-hmac-2025",messageType:"privateav:verification:complete",legacyMessageType:"privateav-verification",popupName:"privateav-verify",docsUrl:"https://docs.privateav.com"};var m=class extends U{constructor(t){super(t,et,k)}},nt="3.4.2";m.VERSION=nt;typeof window!="undefined"&&(N(),L(`${k.name} SDK`));var mt=m;return ct(wt);})();
3
+ if(typeof PrivateAVSDK !== "undefined" && PrivateAVSDK.PrivateAV) { window.PrivateAV = PrivateAVSDK.PrivateAV; window.PrivateAV.VERSION = PrivateAVSDK.VERSION; }
@@ -0,0 +1,156 @@
1
+ /**
2
+ * SDK type definitions (brand-neutral).
3
+ */
4
+ export interface SDKConfig {
5
+ /**
6
+ * Public API key (starts with pk_)
7
+ */
8
+ apiKey: string;
9
+ /**
10
+ * URL to redirect to after successful verification
11
+ * Must be pre-registered in the dashboard
12
+ */
13
+ returnUrl: string;
14
+ /**
15
+ * URL to redirect to if user cancels verification
16
+ * @deprecated This field is no longer used. All redirects go to returnUrl with a status parameter.
17
+ * Kept for backwards compatibility but will be ignored.
18
+ */
19
+ cancelUrl?: string;
20
+ /**
21
+ * Environment to use
22
+ * @default Auto-detected based on hostname (production unless staging detected)
23
+ */
24
+ environment?: 'production' | 'staging';
25
+ /**
26
+ * Verification mode
27
+ * @default 'redirect'
28
+ */
29
+ mode?: 'redirect' | 'new-tab';
30
+ /**
31
+ * Default challenge age (minimum 25)
32
+ * Can be overridden per verification
33
+ */
34
+ defaultChallengeAge?: number;
35
+ /**
36
+ * Default verification mode
37
+ * Can be overridden per verification
38
+ */
39
+ defaultVerificationMode?: 'L1' | 'L2';
40
+ /**
41
+ * Override API URL
42
+ */
43
+ apiUrl?: string;
44
+ /**
45
+ * Override Verify UI URL
46
+ */
47
+ verifyUrl?: string;
48
+ /**
49
+ * Callback when verification completes (new-tab mode only)
50
+ */
51
+ onComplete?: (result: VerificationResult) => void;
52
+ /**
53
+ * Callback when user cancels (new-tab mode only)
54
+ */
55
+ onCancel?: () => void;
56
+ /**
57
+ * Callback for errors
58
+ */
59
+ onError?: (error: Error) => void;
60
+ }
61
+ export interface VerificationOptions {
62
+ /**
63
+ * Minimum age to verify (minimum 25)
64
+ * @default Uses merchant dashboard configuration
65
+ */
66
+ challengeAge?: number;
67
+ /**
68
+ * Verification mode
69
+ * L1: Age estimation allowed if user appears older
70
+ * L2: Full ID verification required
71
+ * @default Uses merchant dashboard configuration
72
+ */
73
+ verificationMode?: 'L1' | 'L2';
74
+ /**
75
+ * External user identifier from merchant system
76
+ * Optional parameter that will be returned with verification results
77
+ * Useful for correlating sessions with merchant user records
78
+ */
79
+ externalUserId?: string;
80
+ /**
81
+ * Skip the intro screen and go directly to camera access
82
+ * Useful for embedded/streamlined flows where user has already consented
83
+ * @default false
84
+ */
85
+ skipIntro?: boolean;
86
+ /**
87
+ * Automatically redirect to returnUrl immediately after successful verification
88
+ * Skips the success screen for seamless embedded experiences
89
+ * @default false
90
+ */
91
+ autoReturn?: boolean;
92
+ }
93
+ export interface VerificationResult {
94
+ /**
95
+ * The session ID that was verified
96
+ */
97
+ sessionId: string;
98
+ /**
99
+ * Binary result: 'verified' or 'failed'
100
+ * Full details available via server-side API
101
+ */
102
+ status: 'verified' | 'failed';
103
+ /**
104
+ * External user identifier if provided during verification
105
+ */
106
+ externalUserId?: string;
107
+ }
108
+ export interface StatePayload {
109
+ merchantId: string;
110
+ sessionId: string;
111
+ returnUrl: string;
112
+ /** @deprecated No longer used - all redirects use returnUrl with status parameter */
113
+ cancelUrl?: string;
114
+ challengeAge?: number;
115
+ verificationMode?: 'L1' | 'L2';
116
+ hasOverrides?: boolean;
117
+ externalUserId?: string;
118
+ timestamp: number;
119
+ apiUrl?: string;
120
+ engineUrl?: string;
121
+ wsUrl?: string;
122
+ environment?: 'production' | 'staging';
123
+ features?: {
124
+ testMode: boolean;
125
+ warmupPeriodMs: number;
126
+ qualityThreshold: number;
127
+ };
128
+ handoffToken?: string;
129
+ sessionToken?: string;
130
+ verifyUrl?: string;
131
+ }
132
+ export interface SessionValidationResponse {
133
+ sessionId: string;
134
+ merchantId: string;
135
+ status: 'verified' | 'failed';
136
+ verified: boolean;
137
+ estimatedAge?: number;
138
+ challengeAge: number;
139
+ verificationMode: 'L1' | 'L2';
140
+ verificationMethod?: 'facial' | 'document' | 'combined';
141
+ timestamp: string;
142
+ expiresAt: string;
143
+ }
144
+ export interface SessionCreationResponse {
145
+ sessionToken: string;
146
+ verifyUrl: string;
147
+ expiresAt: string;
148
+ }
149
+ export interface CreateSessionRequest {
150
+ returnUrl: string;
151
+ cancelUrl?: string;
152
+ challengeAge?: number;
153
+ verificationMode?: 'L1' | 'L2';
154
+ merchantName?: string;
155
+ externalUserId?: string;
156
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Crypto utilities for browser-compatible SDKs.
3
+ *
4
+ * These utilities handle HMAC signing, state parameter protection, and secure
5
+ * token generation for client-side tamper resistance.
6
+ *
7
+ * IMPORTANT SECURITY NOTE:
8
+ * Client-side cryptography provides defense-in-depth but cannot be considered
9
+ * secure against determined attackers. Server-side validation is required for
10
+ * real security.
11
+ */
12
+ /**
13
+ * Generate HMAC-SHA256 signature using Web Crypto API
14
+ */
15
+ export declare function generateHMAC(data: string, secret: string): Promise<string>;
16
+ /**
17
+ * Verify HMAC-SHA256 signature using Web Crypto API
18
+ */
19
+ export declare function verifyHMAC(data: string, signature: string, secret: string): Promise<boolean>;
20
+ /**
21
+ * Generate a secure random token for client-side use
22
+ */
23
+ export declare function generateSecureToken(length?: number): string;
24
+ /**
25
+ * Create signed state parameter with timestamp and integrity protection
26
+ */
27
+ export declare function createSignedState(payload: unknown, hmacSecret: string): Promise<string>;
28
+ /**
29
+ * Verify and parse signed state parameter
30
+ */
31
+ export declare function parseSignedState(signedState: string, hmacSecret: string, maxAge?: number, logLabel?: string): Promise<unknown | null>;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Environment utilities with security enforcement.
3
+ */
4
+ import type { UrlConfig } from '../core/VerificationSDK';
5
+ export declare function getEnvironmentUrl(environment: 'production' | 'staging', urls: UrlConfig): string;
6
+ export declare function getApiUrl(environment: 'production' | 'staging', urls: UrlConfig): string;
7
+ export declare function detectEnvironment(): 'production' | 'staging';
8
+ export declare function validateEnvironmentSecurity(environment: 'production' | 'staging', urls: UrlConfig, logLabel?: string): void;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Polyfills for browser compatibility
3
+ */
4
+ /**
5
+ * Polyfill for crypto.randomUUID (not supported in older browsers)
6
+ */
7
+ export declare function setupPolyfills(): void;
8
+ /**
9
+ * Check browser compatibility and warn about unsupported features
10
+ */
11
+ export declare function checkBrowserCompatibility(logLabel?: string): void;
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Security utilities for the SDK.
3
+ * Enhanced origin validation and security enforcement.
4
+ */
5
+ /**
6
+ * Validate if an origin is trusted for the given environment
7
+ */
8
+ export declare function isOriginTrusted(origin: string, trustedOrigins: string[]): boolean;
9
+ /**
10
+ * Enhanced origin validation with logging and strict allowlist
11
+ */
12
+ export declare function validatePostMessageOrigin(event: MessageEvent, trustedOrigins: string[], allowedCustomOrigins?: string[], logLabel?: string): boolean;
13
+ /**
14
+ * Validate verification message format and content
15
+ */
16
+ export declare function validateVerificationMessage(event: MessageEvent, expectedSessionId: string, expectedMessageType: string, legacyMessageType?: string): {
17
+ isValid: boolean;
18
+ error?: string;
19
+ };
20
+ /**
21
+ * Enforce HTTPS in production environment
22
+ */
23
+ export declare function enforceHTTPS(environment: 'production' | 'staging', logLabel?: string): void;
24
+ /**
25
+ * Validate URL security for return/cancel URLs
26
+ */
27
+ export declare function validateReturnUrl(url: string, environment: 'production' | 'staging', logLabel?: string): {
28
+ isValid: boolean;
29
+ error?: string;
30
+ };
31
+ /**
32
+ * Generate secure session ID with entropy validation
33
+ */
34
+ export declare function generateSecureSessionId(): string;
35
+ /**
36
+ * Rate limiting for verification attempts
37
+ */
38
+ declare class VerificationRateLimit {
39
+ private attempts;
40
+ private readonly maxAttempts;
41
+ private readonly timeWindow;
42
+ isAllowed(identifier: string, logLabel?: string): boolean;
43
+ reset(identifier: string): void;
44
+ }
45
+ export declare const verificationRateLimit: VerificationRateLimit;
46
+ /**
47
+ * Log security events with consistent formatting
48
+ */
49
+ export declare function logSecurityEvent(event: string, metadata: Record<string, unknown>, logLabel?: string): void;
50
+ export {};
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Validation utilities for the SDK
3
+ */
4
+ import type { SDKConfig, StatePayload } from '../types/base';
5
+ export declare const MINIMUM_AGE = 25;
6
+ export declare const MAXIMUM_AGE = 150;
7
+ export declare const MAX_URL_LENGTH = 2048;
8
+ export declare const MAX_API_KEY_LENGTH = 128;
9
+ export declare const STATE_EXPIRY_MS = 600000;
10
+ export interface ValidationContext {
11
+ brandName: string;
12
+ docsUrl: string;
13
+ }
14
+ export declare function validateConfig(config: SDKConfig, context: ValidationContext): void;
15
+ export declare function validateSessionId(sessionId: string): void;
16
+ export declare function validateChallengeAge(age?: number): void;
17
+ export declare function generateState(payload: StatePayload, environment: 'production' | 'staging', hmacSecret: string, logLabel?: string): Promise<string>;
18
+ export declare function parseState(state: string, environment: 'production' | 'staging', hmacSecret: string, logLabel?: string): Promise<StatePayload | null>;