diff --git a/src/tools/auth0/handlers/attackProtection.ts b/src/tools/auth0/handlers/attackProtection.ts index 897ba05f3..144b24ae7 100644 --- a/src/tools/auth0/handlers/attackProtection.ts +++ b/src/tools/auth0/handlers/attackProtection.ts @@ -258,15 +258,26 @@ export default class AttackProtectionHandler extends DefaultAPIHandler { let captcha: Asset | null = null; try { - [botDetection, captcha] = await Promise.all([ - this.client.attackProtection.botDetection.get(), - this.client.attackProtection.captcha.get(), - ]); + botDetection = await this.client.attackProtection.botDetection.get(); + } catch (err) { + if (err.statusCode === 403) { + log.warn( + "Bot detection is not available on this tenant's current plan. Skipping bot detection settings." + ); + } else { + throw err; + } + } + + try { + captcha = await this.client.attackProtection.captcha.get(); } catch (err) { if (err.statusCode === 403) { log.warn( - 'Bot Detection API are not enabled for this tenant. Please verify `scope` or contact Auth0 support to enable this feature.' + "Captcha is not available on this tenant's current plan. Skipping captcha settings." ); + } else { + throw err; } } diff --git a/test/context/yaml/context.test.js b/test/context/yaml/context.test.js index 8806e656a..63e18f3db 100644 --- a/test/context/yaml/context.test.js +++ b/test/context/yaml/context.test.js @@ -321,8 +321,10 @@ describe('#YAML context validation', () => { enabled_locales: ['en'], }, attackProtection: { + botDetection: {}, breachedPasswordDetection: {}, bruteForceProtection: {}, + captcha: {}, suspiciousIpThrottling: {}, }, logStreams: [], @@ -466,8 +468,10 @@ describe('#YAML context validation', () => { enabled_locales: ['en'], }, attackProtection: { + botDetection: {}, breachedPasswordDetection: {}, bruteForceProtection: {}, + captcha: {}, suspiciousIpThrottling: {}, }, logStreams: [], @@ -612,8 +616,10 @@ describe('#YAML context validation', () => { enabled_locales: ['en'], }, attackProtection: { + botDetection: {}, breachedPasswordDetection: {}, bruteForceProtection: {}, + captcha: {}, suspiciousIpThrottling: {}, }, prompts: { diff --git a/test/tools/auth0/handlers/attackProtection.tests.js b/test/tools/auth0/handlers/attackProtection.tests.js index 259766353..50f9335be 100644 --- a/test/tools/auth0/handlers/attackProtection.tests.js +++ b/test/tools/auth0/handlers/attackProtection.tests.js @@ -220,7 +220,7 @@ describe('#attackProtection handler', () => { ]); }); - it('should handle 403 error when fetching bot detection and captcha configs', async () => { + it('should handle 403 error when fetching bot detection and preserve captcha data', async () => { const auth0 = { attackProtection: { botDetection: { @@ -231,11 +231,10 @@ describe('#attackProtection handler', () => { }, }, captcha: { - get: () => { - const err = new Error('Forbidden'); - err.statusCode = 403; - throw err; - }, + get: () => ({ + selected: 'friendly_captcha', + policy: 'always', + }), }, breachedPasswordDetection: { get: () => ({ @@ -277,39 +276,76 @@ describe('#attackProtection handler', () => { const handler = new attackProtection.default({ client: auth0 }); const data = await handler.getType(); - // Should return data without botDetection and captcha - expect(data).to.deep.equal({ - breachedPasswordDetection: { - admin_notification_frequency: [], - enabled: true, - method: 'standard', - shields: [], - }, - bruteForceProtection: { - allowlist: [], - enabled: true, - max_attempts: 10, - mode: 'count_per_identifier_and_ip', - shields: ['block', 'user_notification'], - }, - suspiciousIpThrottling: { - allowlist: ['127.0.0.1'], - enabled: true, - shields: ['block', 'admin_notification'], - stage: { - 'pre-login': { - max_attempts: 100, - rate: 864000, - }, - 'pre-user-registration': { - max_attempts: 50, - rate: 1200, + // botDetection should be skipped, captcha should still be present + expect(data).to.not.have.property('botDetection'); + expect(data).to.have.property('captcha').that.deep.equals({ + selected: 'friendly_captcha', + policy: 'always', + }); + expect(data).to.have.property('breachedPasswordDetection'); + expect(data).to.have.property('bruteForceProtection'); + expect(data).to.have.property('suspiciousIpThrottling'); + }); + + it('should handle 403 error when fetching captcha and preserve bot detection data', async () => { + const auth0 = { + attackProtection: { + botDetection: { + get: () => ({ + bot_detection_level: 'medium', + monitoring_mode_enabled: false, + }), + }, + captcha: { + get: () => { + const err = new Error('Forbidden'); + err.statusCode = 403; + throw err; }, }, + breachedPasswordDetection: { + get: () => ({ + admin_notification_frequency: [], + enabled: true, + method: 'standard', + shields: [], + }), + }, + bruteForceProtection: { + get: () => ({ + allowlist: [], + enabled: true, + max_attempts: 10, + mode: 'count_per_identifier_and_ip', + shields: ['block', 'user_notification'], + }), + }, + suspiciousIpThrottling: { + get: () => ({ + allowlist: ['127.0.0.1'], + enabled: true, + shields: ['block', 'admin_notification'], + stage: { + 'pre-login': { max_attempts: 100, rate: 864000 }, + 'pre-user-registration': { max_attempts: 50, rate: 1200 }, + }, + }), + }, }, - }); - expect(data).to.not.have.property('botDetection'); + }; + + const handler = new attackProtection.default({ client: auth0 }); + const data = await handler.getType(); + + // captcha should be skipped, botDetection should still be present expect(data).to.not.have.property('captcha'); + expect(data).to.have.property('botDetection').that.deep.equals({ + bot_detection_level: 'medium', + monitoring_mode_enabled: false, + }); + expect(data).to.have.property('breachedPasswordDetection'); + expect(data).to.have.property('bruteForceProtection'); + expect(data).to.have.property('suspiciousIpThrottling'); }); it('should skip updates when attackProtection is null', async () => { diff --git a/test/utils.js b/test/utils.js index e25ede903..abd54ee49 100644 --- a/test/utils.js +++ b/test/utils.js @@ -149,12 +149,18 @@ export function mockMgmtClient() { getCustomTextByLanguage: () => Promise.resolve({}), }, attackProtection: { + botDetection: { + get: () => ({}), + }, breachedPasswordDetection: { get: () => ({}), }, bruteForceProtection: { get: () => ({}), }, + captcha: { + get: () => ({}), + }, suspiciousIpThrottling: { get: () => ({}), },