Skip to content

Commit

Permalink
[IAMRISK-2916] Added support for Auth0 v2 captcha provider (#1368)
Browse files Browse the repository at this point in the history
Co-authored-by: Frederik Prijck <[email protected]>
  • Loading branch information
alexkoumarianos-okta and frederikprijck authored Dec 13, 2023
1 parent f54cf1b commit f5e1f71
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 26 deletions.
31 changes: 27 additions & 4 deletions src/web-auth/captcha.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ var HCAPTCHA_PROVIDER = 'hcaptcha';
var FRIENDLY_CAPTCHA_PROVIDER = 'friendly_captcha';
var ARKOSE_PROVIDER = 'arkose';
var AUTH0_PROVIDER = 'auth0';
var AUTH0_V2_CAPTCHA_PROVIDER = 'auth0_v2';
var MAX_RETRY = 3;

var defaults = {
Expand Down Expand Up @@ -50,6 +51,9 @@ var defaults = {
arkose: function () {
return '<div class="arkose" ></div><input type="hidden" name="captcha" />';
},
auth0_v2: function () {
return '<div class="auth0_v2" ></div><input type="hidden" name="captcha" />';
},
error: function () {
return '<div class="error" style="color: red;">Error getting the bot detection challenge. Please contact the system administrator.</div>';
}
Expand Down Expand Up @@ -78,6 +82,8 @@ function globalForCaptchaProvider(provider) {
return window.friendlyChallenge;
case ARKOSE_PROVIDER:
return window.arkose;
case AUTH0_V2_CAPTCHA_PROVIDER:
return window.turnstile;
/* istanbul ignore next */
default:
throw new Error('Unknown captcha provider');
Expand Down Expand Up @@ -120,6 +126,11 @@ function scriptForCaptchaProvider(
siteKey +
'/api.js'
);
case AUTH0_V2_CAPTCHA_PROVIDER:
return (
'https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit&onload=' +
callback
);
/* istanbul ignore next */
default:
throw new Error('Unknown captcha provider');
Expand Down Expand Up @@ -238,6 +249,9 @@ function handleCaptchaProvider(element, options, challenge) {
case ARKOSE_PROVIDER:
captchaClass = '.arkose';
break;
case AUTH0_V2_CAPTCHA_PROVIDER:
captchaClass = '.auth0_v2';
break;
}
var captchaDiv = element.querySelector(captchaClass);

Expand Down Expand Up @@ -285,7 +299,7 @@ function handleCaptchaProvider(element, options, challenge) {
}
});
} else {
widgetId = global.render(captchaDiv, {
var renderParams = {
callback: setValue,
'expired-callback': function () {
setValue();
Expand All @@ -294,7 +308,12 @@ function handleCaptchaProvider(element, options, challenge) {
setValue();
},
sitekey: challenge.siteKey
});
};
if (challenge.provider === AUTH0_V2_CAPTCHA_PROVIDER) {
renderParams.language = options.lang;
renderParams.theme = 'light';
}
widgetId = global.render(captchaDiv, renderParams);
element.setAttribute('data-wid', widgetId);
}
},
Expand All @@ -316,6 +335,7 @@ function handleCaptchaProvider(element, options, challenge) {
* @param {Function} [options.templates.hcaptcha] template function receiving the challenge and returning a string
* @param {Function} [options.templates.friendly_captcha] template function receiving the challenge and returning a string
* @param {Function} [options.templates.arkose] template function receiving the challenge and returning a string
* @param {Function} [options.templates.auth0_v2] template function receiving the challenge and returning a string
* @param {Function} [options.templates.error] template function returning a custom error message when the challenge could not be fetched, receives the error as first argument
* @param {String} [options.lang=en] the ISO code of the language for recaptcha
* @param {Function} [callback] An optional callback called after captcha is loaded
Expand Down Expand Up @@ -343,7 +363,8 @@ function render(auth0Client, element, options, callback) {
challenge.provider === RECAPTCHA_ENTERPRISE_PROVIDER ||
challenge.provider === HCAPTCHA_PROVIDER ||
challenge.provider === FRIENDLY_CAPTCHA_PROVIDER ||
challenge.provider === ARKOSE_PROVIDER
challenge.provider === ARKOSE_PROVIDER ||
challenge.provider === AUTH0_V2_CAPTCHA_PROVIDER
) {
handleCaptchaProvider(element, options, challenge);
}
Expand Down Expand Up @@ -390,6 +411,7 @@ function render(auth0Client, element, options, callback) {
* @param {Function} [options.templates.hcaptcha] template function receiving the challenge and returning a string
* @param {Function} [options.templates.friendly_captcha] template function receiving the challenge and returning a string
* @param {Function} [options.templates.arkose] template function receiving the challenge and returning a string
* @param {Function} [options.templates.auth0_v2] template function receiving the challenge and returning a string
* @param {Function} [options.templates.error] template function returning a custom error message when the challenge could not be fetched, receives the error as first argument
* @param {String} [options.lang=en] the ISO code of the language for recaptcha
* @param {Function} [callback] An optional callback called after captcha is loaded
Expand Down Expand Up @@ -417,7 +439,8 @@ function renderPasswordless(auth0Client, element, options, callback) {
challenge.provider === RECAPTCHA_ENTERPRISE_PROVIDER ||
challenge.provider === HCAPTCHA_PROVIDER ||
challenge.provider === FRIENDLY_CAPTCHA_PROVIDER ||
challenge.provider === ARKOSE_PROVIDER
challenge.provider === ARKOSE_PROVIDER ||
challenge.provider === AUTH0_V2_CAPTCHA_PROVIDER
) {
handleCaptchaProvider(element, options, challenge);
}
Expand Down
2 changes: 2 additions & 0 deletions src/web-auth/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -1151,6 +1151,7 @@ WebAuth.prototype.passwordlessVerify = function (options, cb) {
* @param {Function} [options.templates.hcaptcha] template function receiving the challenge and returning a string
* @param {Function} [options.templates.friendly_captcha] template function receiving the challenge and returning a string
* @param {Function} [options.templates.arkose] template function receiving the challenge and returning a string
* @param {Function} [options.templates.auth0_v2] template function receiving the challenge and returning a string
* @param {Function} [options.templates.error] template function returning a custom error message when the challenge could not be fetched, receives the error as first argument
* @param {String} [options.lang=en] the ISO code of the language for the captcha provider
* @param {captchaLoadedCallback} [callback] An optional callback called after captcha is loaded
Expand All @@ -1175,6 +1176,7 @@ WebAuth.prototype.renderCaptcha = function (element, options, callback) {
* @param {Function} [options.templates.hcaptcha] template function receiving the challenge and returning a string
* @param {Function} [options.templates.friendly_captcha] template function receiving the challenge and returning a string
* @param {Function} [options.templates.arkose] template function receiving the challenge and returning a string
* @param {Function} [options.templates.auth0_v2] template function receiving the challenge and returning a string
* @param {Function} [options.templates.error] template function returning a custom error message when the challenge could not be fetched, receives the error as first argument
* @param {String} [options.lang=en] the ISO code of the language for the captcha provider
* @param {captchaLoadedCallback} [callback] An optional callback called after captcha is loaded
Expand Down
72 changes: 50 additions & 22 deletions test/web-auth/captcha.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -192,12 +192,14 @@ describe('captcha rendering', function () {
const HCAPTCHA_PROVIDER = 'hcaptcha';
const FRIENDLY_CAPTCHA_PROVIDER = 'friendly_captcha';
const ARKOSE_PROVIDER = 'arkose';
const AUTH0_V2_CAPTCHA_PROVIDER = 'auth0_v2';

[
RECAPTCHA_V2_PROVIDER,
RECAPTCHA_ENTERPRISE_PROVIDER,
HCAPTCHA_PROVIDER,
FRIENDLY_CAPTCHA_PROVIDER
FRIENDLY_CAPTCHA_PROVIDER,
AUTH0_V2_CAPTCHA_PROVIDER
].forEach(provider => {
const getScript = () => {
switch (provider) {
Expand All @@ -209,19 +211,19 @@ describe('captcha rendering', function () {
return 'enterprise.js';
case FRIENDLY_CAPTCHA_PROVIDER:
return 'widget.min.js';
case AUTH0_V2_CAPTCHA_PROVIDER:
return 'api.js';
}
};
const getHostname = () => {
switch (provider) {
case RECAPTCHA_V2_PROVIDER:
return 'recaptcha.net';
case RECAPTCHA_ENTERPRISE_PROVIDER:
return 'recaptcha.net';
case HCAPTCHA_PROVIDER:
return 'hcaptcha.com';
case FRIENDLY_CAPTCHA_PROVIDER:
return 'jsdelivr.net';
const hosts = {
[RECAPTCHA_V2_PROVIDER]: 'recaptcha.net',
[RECAPTCHA_ENTERPRISE_PROVIDER]: 'recaptcha.net',
[HCAPTCHA_PROVIDER]: 'hcaptcha.com',
[FRIENDLY_CAPTCHA_PROVIDER]: 'jsdelivr.net',
[AUTH0_V2_CAPTCHA_PROVIDER]: 'cloudflare.com'
}
return hosts[provider];
};
const getSubdomain = () => {
switch (provider) {
Expand All @@ -233,6 +235,8 @@ describe('captcha rendering', function () {
return 'js';
case FRIENDLY_CAPTCHA_PROVIDER:
return 'cdn';
case AUTH0_V2_CAPTCHA_PROVIDER:
return 'challenges';
}
};
const getPath = () => {
Expand All @@ -245,6 +249,8 @@ describe('captcha rendering', function () {
return '1';
case FRIENDLY_CAPTCHA_PROVIDER:
return 'npm/[email protected]';
case AUTH0_V2_CAPTCHA_PROVIDER:
return 'turnstile/v0';
}
};
const setMockGlobal = mock => {
Expand All @@ -261,6 +267,9 @@ describe('captcha rendering', function () {
case FRIENDLY_CAPTCHA_PROVIDER:
window.friendlyChallenge = mock;
break;
case AUTH0_V2_CAPTCHA_PROVIDER:
window.turnstile = mock;
break;
}
};
describe(`when challenge is required and provider is ${provider}`, function () {
Expand Down Expand Up @@ -303,8 +312,13 @@ describe('captcha rendering', function () {
`${getSubdomain()}.${getHostname()}`
);
expect(scriptUrl.pathname).to.equal(`/${getPath()}/${getScript()}`);
if (provider !== FRIENDLY_CAPTCHA_PROVIDER) {
if (
provider !== FRIENDLY_CAPTCHA_PROVIDER &&
provider !== AUTH0_V2_CAPTCHA_PROVIDER
) {
expect(scriptUrl.query.hl).to.equal('en');
}
if (provider !== FRIENDLY_CAPTCHA_PROVIDER) {
expect(scriptUrl.query).to.have.property('onload');
}
});
Expand Down Expand Up @@ -748,12 +762,14 @@ describe('passwordless captcha rendering', function () {
const HCAPTCHA_PROVIDER = 'hcaptcha';
const FRIENDLY_CAPTCHA_PROVIDER = 'friendly_captcha';
const ARKOSE_PROVIDER = 'arkose';
const AUTH0_V2_CAPTCHA_PROVIDER = 'auth0_v2';

[
RECAPTCHA_V2_PROVIDER,
RECAPTCHA_ENTERPRISE_PROVIDER,
HCAPTCHA_PROVIDER,
FRIENDLY_CAPTCHA_PROVIDER
FRIENDLY_CAPTCHA_PROVIDER,
AUTH0_V2_CAPTCHA_PROVIDER
].forEach(provider => {
const getScript = () => {
switch (provider) {
Expand All @@ -765,19 +781,19 @@ describe('passwordless captcha rendering', function () {
return 'enterprise.js';
case FRIENDLY_CAPTCHA_PROVIDER:
return 'widget.min.js';
case AUTH0_V2_CAPTCHA_PROVIDER:
return 'api.js';
}
};
const getHostname = () => {
switch (provider) {
case RECAPTCHA_V2_PROVIDER:
return 'recaptcha.net';
case RECAPTCHA_ENTERPRISE_PROVIDER:
return 'recaptcha.net';
case HCAPTCHA_PROVIDER:
return 'hcaptcha.com';
case FRIENDLY_CAPTCHA_PROVIDER:
return 'jsdelivr.net';
const hosts = {
[RECAPTCHA_V2_PROVIDER]: 'recaptcha.net',
[RECAPTCHA_ENTERPRISE_PROVIDER]: 'recaptcha.net',
[HCAPTCHA_PROVIDER]: 'hcaptcha.com',
[FRIENDLY_CAPTCHA_PROVIDER]: 'jsdelivr.net',
[AUTH0_V2_CAPTCHA_PROVIDER]: 'cloudflare.com'
}
return hosts[provider];
};
const getSubdomain = () => {
switch (provider) {
Expand All @@ -789,6 +805,8 @@ describe('passwordless captcha rendering', function () {
return 'js';
case FRIENDLY_CAPTCHA_PROVIDER:
return 'cdn';
case AUTH0_V2_CAPTCHA_PROVIDER:
return 'challenges';
}
};
const getPath = () => {
Expand All @@ -801,6 +819,8 @@ describe('passwordless captcha rendering', function () {
return '1';
case FRIENDLY_CAPTCHA_PROVIDER:
return 'npm/[email protected]';
case AUTH0_V2_CAPTCHA_PROVIDER:
return 'turnstile/v0';
}
};
const setMockGlobal = mock => {
Expand All @@ -817,6 +837,9 @@ describe('passwordless captcha rendering', function () {
case FRIENDLY_CAPTCHA_PROVIDER:
window.friendlyChallenge = mock;
break;
case AUTH0_V2_CAPTCHA_PROVIDER:
window.turnstile = mock;
break;
}
};
describe(`when challenge is required and provider is ${provider}`, function () {
Expand Down Expand Up @@ -861,8 +884,13 @@ describe('passwordless captcha rendering', function () {
`${getSubdomain()}.${getHostname()}`
);
expect(scriptUrl.pathname).to.equal(`/${getPath()}/${getScript()}`);
if (provider !== FRIENDLY_CAPTCHA_PROVIDER) {
if (
provider !== FRIENDLY_CAPTCHA_PROVIDER &&
provider !== AUTH0_V2_CAPTCHA_PROVIDER
) {
expect(scriptUrl.query.hl).to.equal('en');
}
if (provider !== FRIENDLY_CAPTCHA_PROVIDER) {
expect(scriptUrl.query).to.have.property('onload');
}
});
Expand Down

0 comments on commit f5e1f71

Please sign in to comment.