@descope/flow-scripts 1.0.4 → 1.0.6
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/dist/fingerprint.js +2 -2
- package/dist/fingerprintDescope.js +2 -2
- package/dist/turnstile.js +3 -0
- package/package.json +8 -8
- package/src/fingerprint.ts +1 -2
- package/src/fingerprintDescope.ts +1 -2
- package/src/grecaptcha.ts +0 -2
- package/src/index.spec.ts +14 -5
- package/src/turnstile.ts +180 -0
package/dist/fingerprint.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(
|
|
2
|
-
/*! Source: (c) FingerprintJS and other contributors | https://dev.fingerprint.com/ */
|
|
1
|
+
!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?module.exports=n():"function"==typeof define&&define.amd?define(n):((e="undefined"!=typeof globalThis?globalThis:e||self).descope=e.descope||{},e.descope.fingerprint=n())}(this,(function(){"use strict";var e=function(){return e=Object.assign||function(e){for(var n,t=1,r=arguments.length;t<r;t++)for(var o in n=arguments[t])Object.prototype.hasOwnProperty.call(n,o)&&(e[o]=n[o]);return e},e.apply(this,arguments)};function n(e,n,t,r){return new(t||(t=Promise))((function(o,i){function u(e){try{a(r.next(e))}catch(e){i(e)}}function c(e){try{a(r.throw(e))}catch(e){i(e)}}function a(e){var n;e.done?o(e.value):(n=e.value,n instanceof t?n:new t((function(e){e(n)}))).then(u,c)}a((r=r.apply(e,n||[])).next())}))}"function"==typeof SuppressedError&&SuppressedError;var t={default:"endpoint"},r="Blocked by CSP",o="The endpoint parameter is not a valid URL",i="Failed to load the JS script of the agent",u="9319";function c(e,n){var t,c,a,f,l,s=[],p=(t=function(e){var n=function(e,n,t){if(t||2===arguments.length)for(var r,o=0,i=n.length;o<i;o++)!r&&o in n||(r||(r=Array.prototype.slice.call(n,0,o)),r[o]=n[o]);return e.concat(r||Array.prototype.slice.call(n))}([],e,!0);return{current:function(){return n[0]},postpone:function(){var e=n.shift();void 0!==e&&n.push(e)},exclude:function(){n.shift()}}}(e),f=0,c=function(){return Math.random()*Math.min(3e3,100*Math.pow(2,f++))},a=new Set,[t.current(),function(e,n){var f,l=n instanceof Error?n.message:"";if(l===r||l===o)t.exclude(),f=0;else if(l===u)t.exclude();else if(l===i){var s=Date.now()-e.getTime()<50,p=t.current();p&&s&&!a.has(p)&&(a.add(p),f=0),t.postpone()}else t.postpone();var d=t.current();return void 0===d?void 0:[d,null!=f?f:e.getTime()+c()-Date.now()]}]),d=p[0],v=p[1];if(void 0===d)return Promise.reject(new TypeError("The list of script URL patterns is empty"));var h=function(e){var t=new Date,r=function(n){return s.push({url:e,startedAt:t,finishedAt:new Date,error:n})},o=n(e);return o.then((function(){return r()}),r),o.catch((function(e){if(null!=l||(l=e),s.length>=5)throw l;var n=v(t,e);if(!n)throw l;var r,o=n[0],i=n[1];return(r=i,new Promise((function(e){return setTimeout(e,r)}))).then((function(){return h(o)}))}))};return h(d).then((function(e){return[e,s]}))}var a="https://fpnpmcdn.net/v<version>/<apiKey>/loader_v<loaderVersion>.js",f=a;function l(n){var t;n.scriptUrlPattern;var r=n.token,o=n.apiKey,f=void 0===o?r:o,l=function(e,n){var t={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&n.indexOf(r)<0&&(t[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols){var o=0;for(r=Object.getOwnPropertySymbols(e);o<r.length;o++)n.indexOf(r[o])<0&&Object.prototype.propertyIsEnumerable.call(e,r[o])&&(t[r[o]]=e[r[o]])}return t}(n,["scriptUrlPattern","token","apiKey"]),p=null!==(t=function(e,n){return function(e,n){return Object.prototype.hasOwnProperty.call(e,n)}(e,n)?e[n]:void 0}(n,"scriptUrlPattern"))&&void 0!==t?t:a,d=function(){var e=[],n=function(){e.push({time:new Date,state:document.visibilityState})},t=function(e,n,t,r){return e.addEventListener(n,t,r),function(){return e.removeEventListener(n,t,r)}}(document,"visibilitychange",n);return n(),[e,t]}(),v=d[0],h=d[1];return Promise.resolve().then((function(){if(!f||"string"!=typeof f)throw new Error("API key required");return c(function(e,n){return(Array.isArray(e)?e:[e]).map((function(e){return function(e,n){var t=encodeURIComponent;return e.replace(/<[^<>]+>/g,(function(e){return"<version>"===e?"3":"<apiKey>"===e?t(n):"<loaderVersion>"===e?t("3.11.8"):e}))}(String(e),n)}))}(p,f),s)})).catch((function(e){throw h(),function(e){return e instanceof Error&&e.message===u?new Error(i):e}(e)})).then((function(n){var t=n[0],r=n[1];return h(),t.load(e(e({},l),{ldi:{attempts:r,visibilityStates:v}}))}))}function s(e){return function(e,n,t){var r,o=document,i="securitypolicyviolation",u=function(n){var t=new URL(e,location.href),o=n.blockedURI;o!==t.href&&o!==t.protocol.slice(0,-1)&&o!==t.origin||(r=n,c())};o.addEventListener(i,u);var c=function(){return o.removeEventListener(i,u)};return Promise.resolve().then(n).then((function(e){return c(),e}),(function(e){return new Promise((function(e){var n=new MessageChannel;n.port1.onmessage=function(){return e()},n.port2.postMessage(null)})).then((function(){if(c(),r)return t(r);throw e}))}))}(e,(function(){return function(e){return new Promise((function(n,t){if(function(e){if(URL.prototype)try{return new URL(e,location.href),!1}catch(e){if(e instanceof Error&&"TypeError"===e.name)return!0;throw e}}(e))throw new Error(o);var r=document.createElement("script"),u=function(){var e;return null===(e=r.parentNode)||void 0===e?void 0:e.removeChild(r)},c=document.head||document.getElementsByTagName("head")[0];r.onload=function(){u(),n()},r.onerror=function(){u(),t(new Error(i))},r.async=!0,r.src=e,c.appendChild(r)}))}(e)}),(function(){throw new Error(r)})).then(p)}function p(){var e=window,n="__fpjs_p_l_b",t=e[n];if(function(e,n){var t,r=null===(t=Object.getOwnPropertyDescriptor)||void 0===t?void 0:t.call(Object,e,n);(null==r?void 0:r.configurable)?delete e[n]:r&&!r.writable||(e[n]=void 0)}(e,n),"function"!=typeof(null==t?void 0:t.load))throw new Error(u);return t}
|
|
2
|
+
/*! Source: (c) FingerprintJS and other contributors | https://dev.fingerprint.com/ */return(e,r,o)=>n(void 0,void 0,void 0,(function*(){try{const{publicApiKey:n,useCloudflareIntegration:r,cloudflareScriptUrl:i,cloudflareEndpointUrl:u}=e;let c=[];c=r&&u?[u,t]:[t];let a=[];if(r&&i){const e=new URL(i);a=[`${e.toString()}?apiKey=<apiKey>&version=<version>&loaderVersion=<loaderVersion>`,f]}else a=[f];const s=l({apiKey:n,endpoint:c,scriptUrlPattern:a}),p=yield s,{requestId:d}=yield p.get();o(d)}catch(e){console.warn("Could not load fingerprint",e)}}))}));
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(
|
|
2
|
-
/*! Source: (c) FingerprintJS and other contributors | https://dev.fingerprint.com/ */
|
|
1
|
+
!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?module.exports=n():"function"==typeof define&&define.amd?define(n):((e="undefined"!=typeof globalThis?globalThis:e||self).descope=e.descope||{},e.descope.fingerprintDescope=n())}(this,(function(){"use strict";var e=function(){return e=Object.assign||function(e){for(var n,t=1,r=arguments.length;t<r;t++)for(var o in n=arguments[t])Object.prototype.hasOwnProperty.call(n,o)&&(e[o]=n[o]);return e},e.apply(this,arguments)};function n(e,n,t,r){return new(t||(t=Promise))((function(o,i){function c(e){try{a(r.next(e))}catch(e){i(e)}}function u(e){try{a(r.throw(e))}catch(e){i(e)}}function a(e){var n;e.done?o(e.value):(n=e.value,n instanceof t?n:new t((function(e){e(n)}))).then(c,u)}a((r=r.apply(e,n||[])).next())}))}"function"==typeof SuppressedError&&SuppressedError;var t={default:"endpoint"},r="Blocked by CSP",o="The endpoint parameter is not a valid URL",i="Failed to load the JS script of the agent",c="9319";function u(e,n){var t,u,a,f,s,l=[],p=(t=function(e){var n=function(e,n,t){if(t||2===arguments.length)for(var r,o=0,i=n.length;o<i;o++)!r&&o in n||(r||(r=Array.prototype.slice.call(n,0,o)),r[o]=n[o]);return e.concat(r||Array.prototype.slice.call(n))}([],e,!0);return{current:function(){return n[0]},postpone:function(){var e=n.shift();void 0!==e&&n.push(e)},exclude:function(){n.shift()}}}(e),f=0,u=function(){return Math.random()*Math.min(3e3,100*Math.pow(2,f++))},a=new Set,[t.current(),function(e,n){var f,s=n instanceof Error?n.message:"";if(s===r||s===o)t.exclude(),f=0;else if(s===c)t.exclude();else if(s===i){var l=Date.now()-e.getTime()<50,p=t.current();p&&l&&!a.has(p)&&(a.add(p),f=0),t.postpone()}else t.postpone();var d=t.current();return void 0===d?void 0:[d,null!=f?f:e.getTime()+u()-Date.now()]}]),d=p[0],v=p[1];if(void 0===d)return Promise.reject(new TypeError("The list of script URL patterns is empty"));var h=function(e){var t=new Date,r=function(n){return l.push({url:e,startedAt:t,finishedAt:new Date,error:n})},o=n(e);return o.then((function(){return r()}),r),o.catch((function(e){if(null!=s||(s=e),l.length>=5)throw s;var n=v(t,e);if(!n)throw s;var r,o=n[0],i=n[1];return(r=i,new Promise((function(e){return setTimeout(e,r)}))).then((function(){return h(o)}))}))};return h(d).then((function(e){return[e,l]}))}var a="https://fpnpmcdn.net/v<version>/<apiKey>/loader_v<loaderVersion>.js",f=a;function s(n){var t;n.scriptUrlPattern;var r=n.token,o=n.apiKey,f=void 0===o?r:o,s=function(e,n){var t={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&n.indexOf(r)<0&&(t[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols){var o=0;for(r=Object.getOwnPropertySymbols(e);o<r.length;o++)n.indexOf(r[o])<0&&Object.prototype.propertyIsEnumerable.call(e,r[o])&&(t[r[o]]=e[r[o]])}return t}(n,["scriptUrlPattern","token","apiKey"]),p=null!==(t=function(e,n){return function(e,n){return Object.prototype.hasOwnProperty.call(e,n)}(e,n)?e[n]:void 0}(n,"scriptUrlPattern"))&&void 0!==t?t:a,d=function(){var e=[],n=function(){e.push({time:new Date,state:document.visibilityState})},t=function(e,n,t,r){return e.addEventListener(n,t,r),function(){return e.removeEventListener(n,t,r)}}(document,"visibilitychange",n);return n(),[e,t]}(),v=d[0],h=d[1];return Promise.resolve().then((function(){if(!f||"string"!=typeof f)throw new Error("API key required");return u(function(e,n){return(Array.isArray(e)?e:[e]).map((function(e){return function(e,n){var t=encodeURIComponent;return e.replace(/<[^<>]+>/g,(function(e){return"<version>"===e?"3":"<apiKey>"===e?t(n):"<loaderVersion>"===e?t("3.11.8"):e}))}(String(e),n)}))}(p,f),l)})).catch((function(e){throw h(),function(e){return e instanceof Error&&e.message===c?new Error(i):e}(e)})).then((function(n){var t=n[0],r=n[1];return h(),t.load(e(e({},s),{ldi:{attempts:r,visibilityStates:v}}))}))}function l(e){return function(e,n,t){var r,o=document,i="securitypolicyviolation",c=function(n){var t=new URL(e,location.href),o=n.blockedURI;o!==t.href&&o!==t.protocol.slice(0,-1)&&o!==t.origin||(r=n,u())};o.addEventListener(i,c);var u=function(){return o.removeEventListener(i,c)};return Promise.resolve().then(n).then((function(e){return u(),e}),(function(e){return new Promise((function(e){var n=new MessageChannel;n.port1.onmessage=function(){return e()},n.port2.postMessage(null)})).then((function(){if(u(),r)return t(r);throw e}))}))}(e,(function(){return function(e){return new Promise((function(n,t){if(function(e){if(URL.prototype)try{return new URL(e,location.href),!1}catch(e){if(e instanceof Error&&"TypeError"===e.name)return!0;throw e}}(e))throw new Error(o);var r=document.createElement("script"),c=function(){var e;return null===(e=r.parentNode)||void 0===e?void 0:e.removeChild(r)},u=document.head||document.getElementsByTagName("head")[0];r.onload=function(){c(),n()},r.onerror=function(){c(),t(new Error(i))},r.async=!0,r.src=e,u.appendChild(r)}))}(e)}),(function(){throw new Error(r)})).then(p)}function p(){var e=window,n="__fpjs_p_l_b",t=e[n];if(function(e,n){var t,r=null===(t=Object.getOwnPropertyDescriptor)||void 0===t?void 0:t.call(Object,e,n);(null==r?void 0:r.configurable)?delete e[n]:r&&!r.writable||(e[n]=void 0)}(e,n),"function"!=typeof(null==t?void 0:t.load))throw new Error(c);return t}
|
|
2
|
+
/*! Source: (c) FingerprintJS and other contributors | https://dev.fingerprint.com/ */return(e,r,o)=>n(void 0,void 0,void 0,(function*(){try{const{customDomain:n,publicApiKey:i,cloudflareEndpointPath:c,cloudflareScriptPath:u}=e,{baseUrl:a}=r;let l;l=n?`https://${n}`:a||"https://api.descope.com";const p=new URL(l);p.pathname=c;const d=new URL(l);d.pathname=u;const v=`${d.toString()}?apiKey=<apiKey>&version=<version>&loaderVersion=<loaderVersion>`,h=s({apiKey:i,endpoint:[p.toString(),t],scriptUrlPattern:[v,f]}),y=yield h,{requestId:m}=yield y.get();o(m)}catch(e){console.warn("Could not load descope fingerprint",e)}}))}));
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):((e="undefined"!=typeof globalThis?globalThis:e||self).descope=e.descope||{},e.descope.turnstile=t())}(this,(function(){"use strict";function e(e,t,n,o){return new(n||(n=Promise))((function(r,s){function c(e){try{a(o.next(e))}catch(e){s(e)}}function i(e){try{a(o.throw(e))}catch(e){s(e)}}function a(e){var t;e.done?r(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(c,i)}a((o=o.apply(e,t||[])).next())}))}"function"==typeof SuppressedError&&SuppressedError;
|
|
2
|
+
/*! Source: (c) Descope and Cloudflare | https://developers.cloudflare.com/turnstile/ */
|
|
3
|
+
const t=105e3;return(n,o,r)=>{let s=(new Date).getTime();const c=()=>globalThis.turnstile;let i,a;const l=(e,t)=>{n.siteKey&&(null==e||e.execute(a,{action:"load"}),s=(new Date).getTime())},u=(e,n)=>{try{e.ready((()=>{l(e)}))}catch(t){try{l(e)}catch(e){console.warn("could not execute turnstile",e)}}finally{i=setTimeout((()=>{u(e,n)}),t)}},d=(()=>{const e=document.createElement("div");return e.style.display="none",e.id="descope-turnstile",e.className="cf-turnstile",document.body.appendChild(e)})(),p=()=>{const e=c();e&&(e.reset(a),u(e,d))};return window.onloadTurnstileCallback=()=>{const e=d;if(e.hasChildNodes())return;const t=c();t&&setTimeout((()=>{a=t.render(e,{sitekey:n.siteKey,execution:"execute",appearance:n.appearance||"execute",callback:e=>r(e)}),u(t,e)}),0)},(()=>{const e=document.createElement("script");e.src=(()=>{const e=new URL("https://challenges.cloudflare.com");return e.pathname+="turnstile/v0/api.js",e.searchParams.append("onload","onloadTurnstileCallback"),e.searchParams.append("render","explicit"),e.toString()})(),e.async=!0,e.id="turnstile-script",e.defer=!1,document.body.appendChild(e)})(),{stop:()=>{clearTimeout(i)},start:p,refresh:()=>e(void 0,void 0,void 0,(function*(){if(Date.now()-s>t){const e=s;return p(),new Promise((t=>{const n=setTimeout((()=>{console.warn("Turnstile token refresh timed out"),t()}),5e3),o=()=>{s!==e?(clearTimeout(n),t()):setTimeout(o,150)};o()}))}return Promise.resolve()}))}}}));
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@descope/flow-scripts",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.6",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "https://github.com/descope/content"
|
|
@@ -14,21 +14,21 @@
|
|
|
14
14
|
"test": "jest"
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {
|
|
17
|
-
"@rollup/plugin-commonjs": "28.0.
|
|
18
|
-
"@rollup/plugin-node-resolve": "16.0.
|
|
17
|
+
"@rollup/plugin-commonjs": "28.0.3",
|
|
18
|
+
"@rollup/plugin-node-resolve": "16.0.1",
|
|
19
19
|
"@rollup/plugin-terser": "0.4.4",
|
|
20
20
|
"@rollup/plugin-typescript": "12.1.2",
|
|
21
21
|
"@testing-library/dom": "10.4.0",
|
|
22
|
-
"@testing-library/react": "16.
|
|
22
|
+
"@testing-library/react": "16.3.0",
|
|
23
23
|
"@types/jest": "29.5.14",
|
|
24
|
-
"eslint": "9.
|
|
24
|
+
"eslint": "9.26.0",
|
|
25
25
|
"eslint-plugin-import": "2.31.0",
|
|
26
26
|
"jest": "29.7.0",
|
|
27
27
|
"jest-environment-jsdom": "29.7.0",
|
|
28
|
-
"rollup": "4.
|
|
29
|
-
"ts-jest": "29.3.
|
|
28
|
+
"rollup": "4.40.0",
|
|
29
|
+
"ts-jest": "29.3.2",
|
|
30
30
|
"ts-node": "10.9.2",
|
|
31
|
-
"typescript": "5.
|
|
31
|
+
"typescript": "5.8.3"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@fingerprintjs/fingerprintjs-pro": "3.11.8"
|
package/src/fingerprint.ts
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
defaultScriptUrlPattern,
|
|
7
7
|
} from '@fingerprintjs/fingerprintjs-pro';
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
const loadFingerprint = async (
|
|
10
10
|
initArgs: {
|
|
11
11
|
publicApiKey: string;
|
|
12
12
|
useCloudflareIntegration: boolean;
|
|
@@ -51,7 +51,6 @@ export const loadFingerprint = async (
|
|
|
51
51
|
const { requestId } = await agent.get();
|
|
52
52
|
onTokenReady(requestId);
|
|
53
53
|
} catch (ex) {
|
|
54
|
-
// eslint-disable-next-line no-console
|
|
55
54
|
console.warn('Could not load fingerprint', ex);
|
|
56
55
|
}
|
|
57
56
|
};
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
defaultScriptUrlPattern,
|
|
7
7
|
} from '@fingerprintjs/fingerprintjs-pro';
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
const loadFingerprint = async (
|
|
10
10
|
initArgs: {
|
|
11
11
|
customDomain?: string;
|
|
12
12
|
publicApiKey: string;
|
|
@@ -57,7 +57,6 @@ export const loadFingerprint = async (
|
|
|
57
57
|
const { requestId } = await agent.get();
|
|
58
58
|
onTokenReady(requestId);
|
|
59
59
|
} catch (ex) {
|
|
60
|
-
// eslint-disable-next-line no-console
|
|
61
60
|
console.warn('Could not load descope fingerprint', ex);
|
|
62
61
|
}
|
|
63
62
|
};
|
package/src/grecaptcha.ts
CHANGED
|
@@ -59,7 +59,6 @@ const loadGRecaptcha = (
|
|
|
59
59
|
?.execute(recaptchaWidgetId, { action: 'load' })
|
|
60
60
|
.then((token: string, e: any) => {
|
|
61
61
|
if (e) {
|
|
62
|
-
// eslint-disable-next-line no-console
|
|
63
62
|
console.warn('could not execute recaptcha', e);
|
|
64
63
|
} else {
|
|
65
64
|
onTokenReady(token);
|
|
@@ -112,7 +111,6 @@ const loadGRecaptcha = (
|
|
|
112
111
|
return new Promise<void>((resolve) => {
|
|
113
112
|
// Set a timeout to prevent indefinite waiting
|
|
114
113
|
const timeout = setTimeout(() => {
|
|
115
|
-
// eslint-disable-next-line no-console
|
|
116
114
|
console.warn('reCAPTCHA token refresh timed out');
|
|
117
115
|
resolve(); // Resolve anyway to prevent blocking form submission
|
|
118
116
|
}, 5000); // 5 second timeout for token refresh
|
package/src/index.spec.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
/* eslint-disable import/no-namespace */
|
|
2
1
|
import { waitFor } from '@testing-library/dom';
|
|
3
2
|
import * as fp from '@fingerprintjs/fingerprintjs-pro';
|
|
3
|
+
import fs from 'fs';
|
|
4
4
|
|
|
5
5
|
const urlPattern =
|
|
6
6
|
'?apiKey=<apiKey>&version=<version>&loaderVersion=<loaderVersion>';
|
|
@@ -13,15 +13,12 @@ async function loadSdkScript(scriptId: string) {
|
|
|
13
13
|
res = await import('./forter');
|
|
14
14
|
return res.default;
|
|
15
15
|
case 'fingerprint':
|
|
16
|
-
// eslint-disable-next-line no-case-declarations
|
|
17
16
|
res = await import('./fingerprint');
|
|
18
17
|
return res.default;
|
|
19
18
|
case 'fingerprintDescope':
|
|
20
|
-
// eslint-disable-next-line no-case-declarations
|
|
21
19
|
res = await import('./fingerprintDescope');
|
|
22
20
|
return res.default;
|
|
23
21
|
case 'grecaptcha':
|
|
24
|
-
// eslint-disable-next-line no-case-declarations
|
|
25
22
|
res = await import('./grecaptcha');
|
|
26
23
|
return res.default;
|
|
27
24
|
default:
|
|
@@ -32,6 +29,19 @@ async function loadSdkScript(scriptId: string) {
|
|
|
32
29
|
global.fetch = mockFetch;
|
|
33
30
|
|
|
34
31
|
describe('scripts', () => {
|
|
32
|
+
describe('all', () => {
|
|
33
|
+
test('should load all scripts and ensure only one export', async () => {
|
|
34
|
+
fs.readdirSync('./src').forEach(async (file) => {
|
|
35
|
+
if (file.endsWith('.ts') && !file.includes('spec')) {
|
|
36
|
+
const scriptId = file.replace('.ts', '');
|
|
37
|
+
|
|
38
|
+
const m = await import(`./${scriptId}`);
|
|
39
|
+
expect(Object.keys(m)).toHaveLength(1);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
})
|
|
44
|
+
|
|
35
45
|
describe('forter', () => {
|
|
36
46
|
it('should run forter', async () => {
|
|
37
47
|
const forterModule = await loadSdkScript('forter');
|
|
@@ -45,7 +55,6 @@ describe('scripts', () => {
|
|
|
45
55
|
// the script changes the id attribute to the siteId
|
|
46
56
|
expect(script?.getAttribute('id')).toBeTruthy();
|
|
47
57
|
// get textContent and ensure it contains the script id
|
|
48
|
-
// eslint-disable-next-line jest-dom/prefer-to-have-text-content
|
|
49
58
|
expect(script?.textContent).toContain('some-site-id');
|
|
50
59
|
|
|
51
60
|
// trigger 'ftr:tokenReady' event and ensure that the tokenChangedFn is called
|
package/src/turnstile.ts
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/*! Source: (c) Descope and Cloudflare | https://developers.cloudflare.com/turnstile/ */
|
|
2
|
+
|
|
3
|
+
// define the global interface for the turnstile object
|
|
4
|
+
declare global {
|
|
5
|
+
interface Window {
|
|
6
|
+
turnstile?: {
|
|
7
|
+
ready: (callback: () => void) => void;
|
|
8
|
+
execute: (widgetId: any, options: { action: string }) => Promise<string>;
|
|
9
|
+
render: (container: HTMLElement, parameters: any) => any;
|
|
10
|
+
reset: (widgetId: any) => void;
|
|
11
|
+
};
|
|
12
|
+
onloadTurnstileCallback: () => void;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Token refresh time: 105 seconds (2 minutes minus 15 seconds)
|
|
17
|
+
// Set to refresh the token shortly before expiration to ensure
|
|
18
|
+
// we always have a valid token when submitting the form
|
|
19
|
+
const TOKEN_REFRESH_TIME_MS = 105000;
|
|
20
|
+
|
|
21
|
+
const loadTurnstile = (
|
|
22
|
+
initArgs: {
|
|
23
|
+
appearance: boolean;
|
|
24
|
+
siteKey: string;
|
|
25
|
+
|
|
26
|
+
},
|
|
27
|
+
_inputs: { baseUrl?: string },
|
|
28
|
+
onTokenReady: (token: string) => void,
|
|
29
|
+
) => {
|
|
30
|
+
let lastTokenFetchTime = new Date().getTime();
|
|
31
|
+
|
|
32
|
+
const getScriptURL = () => {
|
|
33
|
+
const url = new URL('https://challenges.cloudflare.com');
|
|
34
|
+
url.pathname += 'turnstile/v0/api.js';
|
|
35
|
+
url.searchParams.append('onload', 'onloadTurnstileCallback');
|
|
36
|
+
url.searchParams.append('render', 'explicit');
|
|
37
|
+
return url.toString();
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const loadScript = () => {
|
|
41
|
+
const script = document.createElement('script');
|
|
42
|
+
script.src = getScriptURL();
|
|
43
|
+
script.async = true;
|
|
44
|
+
script.id = 'turnstile-script';
|
|
45
|
+
script.defer = false;
|
|
46
|
+
document.body.appendChild(script);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const getInstance = () => globalThis.turnstile;
|
|
50
|
+
|
|
51
|
+
let timer;
|
|
52
|
+
let turnstileWidgetId;
|
|
53
|
+
|
|
54
|
+
const executeNewToken = (turnstileInstance: any, currentNode: HTMLDivElement) => {
|
|
55
|
+
if (!initArgs.siteKey) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
turnstileInstance?.execute(turnstileWidgetId, { action: 'load' });
|
|
59
|
+
lastTokenFetchTime = new Date().getTime();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const getNewToken = (turnstileInstance: any, currentNode: HTMLDivElement) => {
|
|
63
|
+
// sometimes the turnstile instance may return configuration error
|
|
64
|
+
// on the first call, so we need to retry
|
|
65
|
+
try {
|
|
66
|
+
turnstileInstance.ready(() => {
|
|
67
|
+
executeNewToken(turnstileInstance, currentNode);
|
|
68
|
+
});
|
|
69
|
+
} catch (e) {
|
|
70
|
+
try {
|
|
71
|
+
executeNewToken(turnstileInstance, currentNode);
|
|
72
|
+
} catch (e) {
|
|
73
|
+
console.warn('could not execute turnstile', e);
|
|
74
|
+
}
|
|
75
|
+
} finally {
|
|
76
|
+
// Set a timeout to refresh the token before it expires
|
|
77
|
+
timer = setTimeout(() => {
|
|
78
|
+
getNewToken(turnstileInstance, currentNode);
|
|
79
|
+
}, TOKEN_REFRESH_TIME_MS);
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const stopTimer = () => {
|
|
84
|
+
clearTimeout(timer);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const createTurnstileElement = () => {
|
|
88
|
+
const turnstileEle = document.createElement('div');
|
|
89
|
+
turnstileEle.style.display = 'none';
|
|
90
|
+
turnstileEle.id = 'descope-turnstile';
|
|
91
|
+
turnstileEle.className = 'cf-turnstile'
|
|
92
|
+
return document.body.appendChild(turnstileEle);
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const elementRef = createTurnstileElement();
|
|
96
|
+
|
|
97
|
+
const resumeScriptExecution = () => {
|
|
98
|
+
const turnstileInstance = getInstance();
|
|
99
|
+
if (!turnstileInstance) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
turnstileInstance.reset(turnstileWidgetId);
|
|
103
|
+
getNewToken(turnstileInstance, elementRef);
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Checks if the turnstile token needs refreshing and refreshes it if necessary
|
|
108
|
+
* This is called before form submission to ensure we have a valid token
|
|
109
|
+
* @returns Promise that resolves when token is refreshed or if refresh isn't needed
|
|
110
|
+
*/
|
|
111
|
+
const refreshIfTokenExpired = async (): Promise<void> => {
|
|
112
|
+
const currentTime = Date.now();
|
|
113
|
+
const timeDiff = currentTime - lastTokenFetchTime;
|
|
114
|
+
|
|
115
|
+
if (timeDiff > TOKEN_REFRESH_TIME_MS) {
|
|
116
|
+
const prev = lastTokenFetchTime;
|
|
117
|
+
resumeScriptExecution();
|
|
118
|
+
|
|
119
|
+
// Return a promise that resolves once the token is refreshed or times out
|
|
120
|
+
return new Promise<void>((resolve) => {
|
|
121
|
+
// Set a timeout to prevent indefinite waiting
|
|
122
|
+
const timeout = setTimeout(() => {
|
|
123
|
+
console.warn('Turnstile token refresh timed out');
|
|
124
|
+
resolve(); // Resolve anyway to prevent blocking form submission
|
|
125
|
+
}, 5000); // 5 second timeout for token refresh
|
|
126
|
+
|
|
127
|
+
const checkToken = () => {
|
|
128
|
+
if (lastTokenFetchTime !== prev) {
|
|
129
|
+
clearTimeout(timeout);
|
|
130
|
+
resolve();
|
|
131
|
+
} else {
|
|
132
|
+
setTimeout(checkToken, 150);
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
checkToken();
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// If no refresh is needed, return a resolved promise
|
|
141
|
+
return Promise.resolve();
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const createOnLoadScript = () => {
|
|
145
|
+
window.onloadTurnstileCallback = () => {
|
|
146
|
+
const currentNode = elementRef;
|
|
147
|
+
|
|
148
|
+
if (currentNode.hasChildNodes()) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const turnstileInstance = getInstance();
|
|
153
|
+
|
|
154
|
+
if (!turnstileInstance) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
setTimeout(() => {
|
|
159
|
+
turnstileWidgetId = turnstileInstance.render(currentNode, {
|
|
160
|
+
sitekey: initArgs.siteKey,
|
|
161
|
+
execution: 'execute',
|
|
162
|
+
appearance: initArgs.appearance || 'execute',
|
|
163
|
+
callback: (token: string) => onTokenReady(token),
|
|
164
|
+
});
|
|
165
|
+
getNewToken(turnstileInstance, currentNode);
|
|
166
|
+
}, 0);
|
|
167
|
+
};
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
createOnLoadScript();
|
|
171
|
+
loadScript();
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
stop: stopTimer,
|
|
175
|
+
start: resumeScriptExecution,
|
|
176
|
+
refresh: refreshIfTokenExpired,
|
|
177
|
+
};
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
export default loadTurnstile;
|