Skip to content

Commit

Permalink
[IAMRISK-3539] Use signup captcha enforcement endpoint for signup flow
Browse files Browse the repository at this point in the history
  • Loading branch information
TSLarson committed Oct 2, 2024
1 parent 1cf70cc commit 9198b14
Show file tree
Hide file tree
Showing 13 changed files with 97 additions and 34 deletions.
22 changes: 12 additions & 10 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -121,11 +121,11 @@
"webpack-dev-server": "^4.11.1"
},
"dependencies": {
"auth0-js": "^9.26.0",
"auth0-js": "^9.27.0",
"auth0-password-policies": "^1.0.2",
"blueimp-md5": "^2.19.0",
"classnames": "^2.3.2",
"dompurify": "^2.3.12",
"dompurify": "^2.5.4",
"immutable": "^3.7.6",
"jsonp": "^0.2.1",
"password-sheriff": "^1.1.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ exports[`SignUpPane shows the Captcha pane 1`] = `
/>
<CaptchaPane
error={false}
flow="signup"
i18n={
{
"html": [Function],
Expand Down Expand Up @@ -241,6 +242,7 @@ exports[`SignUpPane shows the Captcha pane for SSO (ADFS) connections 1`] = `
/>
<CaptchaPane
error={false}
flow="signup"
i18n={
{
"html": [Function],
Expand Down
10 changes: 6 additions & 4 deletions src/__tests__/engine/classic/sign_up_pane.test.jsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import React from 'react';
import { expectComponent, mockComponent } from 'testUtils';
import { expectShallowComponent } from '../../testUtils';
import { Flow } from '../../../connection/captcha';

jest.mock('field/email/email_pane', () => mockComponent('email_pane'));
jest.mock('field/password/password_pane', () => mockComponent('password_pane'));
jest.mock('field/username/username_pane', () => mockComponent('username_pane'));
jest.mock('field/custom_input', () => mockComponent('custom_input'));

jest.mock('core/index', () => ({
captcha: jest.fn()
signupCaptcha: jest.fn()
}));

jest.mock('engine/classic', () => ({
Expand Down Expand Up @@ -38,6 +39,7 @@ describe('SignUpPane', () => {
str: (...keys) => keys.join(','),
html: (...keys) => keys.join(',')
},
flow: Flow.SIGNUP,
model: 'model',
emailInputPlaceholder: 'emailInputPlaceholder',
onlyEmail: true,
Expand All @@ -58,7 +60,7 @@ describe('SignUpPane', () => {
});

it('shows the Captcha pane', () => {
require('core/index').captcha.mockReturnValue({
require('core/index').signupCaptcha.mockReturnValue({
get() {
return true;
}
Expand All @@ -72,7 +74,7 @@ describe('SignUpPane', () => {
});

it('hides the Captcha pane for SSO connections', () => {
require('core/index').captcha.mockReturnValue({
require('core/index').signupCaptcha.mockReturnValue({
get() {
return true;
}
Expand All @@ -86,7 +88,7 @@ describe('SignUpPane', () => {
});

it('shows the Captcha pane for SSO (ADFS) connections', () => {
require('core/index').captcha.mockReturnValue({
require('core/index').signupCaptcha.mockReturnValue({
get() {
return true;
}
Expand Down
16 changes: 14 additions & 2 deletions src/connection/captcha.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ import webApi from '../core/web_api';

export const Flow = Object.freeze({
DEFAULT: 'default',
SIGNUP: 'signup',
PASSWORDLESS: 'passwordless',
PASSWORD_RESET: 'password_reset',
});

/**
* Return the captcha config object based on the type of flow.
*
*
* @param {Object} m model
* @param {Flow} flow Which flow the captcha is being rendered in
*/
Expand All @@ -21,6 +22,8 @@ export function getCaptchaConfig(m, flow) {
return l.passwordResetCaptcha(m);
} else if (flow === Flow.PASSWORDLESS) {
return l.passwordlessCaptcha(m);
} else if (flow === Flow.SIGNUP) {
return l.signupCaptcha(m);
} else {
return l.captcha(m);
}
Expand All @@ -42,7 +45,7 @@ export function showMissingCaptcha(m, id, flow = Flow.DEFAULT) {
captchaConfig.get('provider') === 'hcaptcha' ||
captchaConfig.get('provider') === 'auth0_v2' ||
captchaConfig.get('provider') === 'friendly_captcha' ||
captchaConfig.get('provider') === 'arkose'
captchaConfig.get('provider') === 'arkose'
) ? 'invalid_recaptcha' : 'invalid_captcha';

const errorMessage = i18n.html(m, ['error', 'login', captchaError]);
Expand Down Expand Up @@ -110,6 +113,15 @@ export function swapCaptcha(id, flow, wasInvalid, next) {
next();
}
});
} else if (flow === Flow.SIGNUP) {
return webApi.getSignupChallenge(id, (err, newCaptcha) => {
if (!err && newCaptcha) {
swap(updateEntity, 'lock', id, l.setSignupChallenge, newCaptcha, wasInvalid);
}
if (next) {
next();
}
});
} else {
return webApi.getChallenge(id, (err, newCaptcha) => {
if (!err && newCaptcha) {
Expand Down
14 changes: 7 additions & 7 deletions src/connection/database/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,9 @@ export function signUp(id) {
autoLogin: shouldAutoLogin(m)
};

const isCaptchaValid = setCaptchaParams(m, params, Flow.DEFAULT, fields);
const isCaptchaValid = setCaptchaParams(m, params, Flow.SIGNUP, fields);
if (!isCaptchaValid) {
return showMissingCaptcha(m, id);
return showMissingCaptcha(m, id, Flow.SIGNUP);
}

if (databaseConnectionRequiresUsername(m)) {
Expand Down Expand Up @@ -131,7 +131,7 @@ export function signUp(id) {

const wasInvalidCaptcha = error && error.code === 'invalid_captcha';

swapCaptcha(id, Flow.DEFAULT, wasInvalidCaptcha, () => {
swapCaptcha(id, Flow.SIGNUP, wasInvalidCaptcha, () => {
setTimeout(() => signUpError(id, error), 250);
});
};
Expand Down Expand Up @@ -290,7 +290,7 @@ export function resetPasswordSuccess(id) {
function resetPasswordError(id, error) {
const m = read(getEntity, 'lock', id);
let key = error.code;

if (error.code === 'invalid_captcha') {
const captchaConfig = l.passwordResetCaptcha(m);
key = (
Expand All @@ -302,7 +302,7 @@ function resetPasswordError(id, error) {
const errorMessage =
i18n.html(m, ['error', 'forgotPassword', key]) ||
i18n.html(m, ['error', 'forgotPassword', 'lock.fallback']);

swapCaptcha(id, Flow.PASSWORD_RESET, error.code === 'invalid_captcha', () => {
swap(updateEntity, 'lock', id, l.setSubmitting, false, errorMessage);
});
Expand All @@ -322,11 +322,11 @@ export function showLoginActivity(id, fields = ['password']) {

export function showSignUpActivity(id, fields = ['password']) {
const m = read(getEntity, 'lock', id);
const captchaConfig = l.captcha(m);
const captchaConfig = l.signupCaptcha(m);
if (captchaConfig && captchaConfig.get('provider') === 'arkose') {
swap(updateEntity, 'lock', id, setScreen, 'signUp', fields);
} else {
swapCaptcha(id, 'login', false, () => {
swapCaptcha(id, Flow.SIGNUP, false, () => {
swap(updateEntity, 'lock', id, setScreen, 'signUp', fields);
});
}
Expand Down
9 changes: 9 additions & 0 deletions src/core/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,11 @@ export function setCaptcha(m, value, wasInvalid) {
return set(m, 'captcha', Immutable.fromJS(value));
}

export function setSignupChallenge(m, value, wasInvalid) {
m = captchaField.reset(m, wasInvalid);
return set(m, 'signupCaptcha', Immutable.fromJS(value));
}

export function setPasswordlessCaptcha(m, value, wasInvalid) {
m = captchaField.reset(m, wasInvalid);
return set(m, 'passwordlessCaptcha', Immutable.fromJS(value));
Expand All @@ -435,6 +440,10 @@ export function captcha(m) {
return get(m, 'captcha');
}

export function signupCaptcha(m) {
return get(m, 'signupCaptcha');
}

export function passwordlessCaptcha(m) {
return get(m, 'passwordlessCaptcha');
}
Expand Down
10 changes: 10 additions & 0 deletions src/core/remote_data.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { isADEnabled } from '../connection/enterprise'; // shouldn't depend on t
import sync, { isSuccess } from '../sync';
import webApi from './web_api';
import { setCaptcha, setPasswordlessCaptcha, setPasswordResetCaptcha } from '../core/index';
import { setSignupChallenge } from './index';

export function syncRemoteData(m) {
if (l.useTenantInfo(m)) {
Expand Down Expand Up @@ -60,6 +61,15 @@ export function syncRemoteData(m) {
successFn: setCaptcha
});

m = sync(m, 'signupCaptcha', {
syncFn: (m, cb) => {
webApi.getSignupChallenge(m.get('id'), (err, r) => {
cb(null, r);
});
},
successFn: setSignupChallenge
});

m = sync(m, 'passwordlessCaptcha', {
syncFn: (m, cb) => {
webApi.getPasswordlessChallenge(m.get('id'), (err, r) => {
Expand Down
4 changes: 4 additions & 0 deletions src/core/web_api.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ class Auth0WebAPI {
return this.clients[lockID].getChallenge(callback);
}

getSignupChallenge(lockID, callback) {
return this.clients[lockID].getSignupChallenge(callback);
}

getPasswordlessChallenge(lockID, callback) {
return this.clients[lockID].getPasswordlessChallenge(callback);
}
Expand Down
4 changes: 4 additions & 0 deletions src/core/web_api/p2_api.js
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,10 @@ class Auth0APIClient {
return this.client.client.getChallenge(...params);
}

getSignupChallenge(...params) {
return this.client.client.dbConnection.getSignupChallenge(...params);
}

getPasswordlessChallenge(...params) {
return this.client.client.passwordless.getChallenge(...params);
}
Expand Down
6 changes: 3 additions & 3 deletions src/engine/classic/sign_up_pane.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,10 @@ export default class SignUpPane extends React.Component {
));

const captchaPane =
l.captcha(model) &&
l.captcha(model).get('required') &&
l.signupCaptcha(model) &&
l.signupCaptcha(model).get('required') &&
(isHRDDomain(model, databaseUsernameValue(model)) || !sso) ? (
<CaptchaPane i18n={i18n} lock={model} onReload={() => swapCaptcha(l.id(model), Flow.DEFAULT, false)} />
<CaptchaPane i18n={i18n} lock={model} flow={Flow.SIGNUP} onReload={() => swapCaptcha(l.id(model), Flow.SIGNUP, false)} />
) : null;

const passwordPane = !onlyEmail && (
Expand Down
16 changes: 10 additions & 6 deletions test/captcha_signup.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ describe('captcha on signup', function () {
describe('svg-captcha', () => {
describe('when the api returns a new challenge', function () {
beforeEach(function (done) {
this.stub = h.stubGetChallenge([svgCaptchaRequiredResponse1, svgCaptchaRequiredResponse2]);
this.stub = h.stubGetSignupChallenge([svgCaptchaRequiredResponse1, svgCaptchaRequiredResponse2]);
this.lock = h.displayLock('', lockOpts, done);
});

Expand Down Expand Up @@ -88,7 +88,7 @@ describe('captcha on signup', function () {

describe('when the challenge api returns required: false', function () {
beforeEach(function (done) {
h.stubGetChallenge({
h.stubGetSignupChallenge({
required: false
});
this.lock = h.displayLock('', lockOpts, done);
Expand All @@ -110,7 +110,7 @@ describe('captcha on signup', function () {
});

h.waitForEmailAndPasswordInput(this.lock, () => {
h.stubGetChallenge(svgCaptchaRequiredResponse1);
h.stubGetSignupChallenge(svgCaptchaRequiredResponse1);
h.fillEmailInput(this.lock, '[email protected]');
h.fillComplexPassword(this.lock);
h.submitForm(this.lock);
Expand All @@ -127,7 +127,7 @@ describe('captcha on signup', function () {
describe('recaptcha', () => {
describe('when the api returns a new challenge', function () {
beforeEach(function (done) {
this.stub = h.stubGetChallenge([recaptchav2Response]);
this.stub = h.stubGetSignupChallenge([recaptchav2Response]);
this.lock = h.displayLock('', lockOpts, done);
});

Expand Down Expand Up @@ -157,10 +157,12 @@ describe('captcha on signup', function () {

describe('when the challenge api returns required: false', function () {
let notRequiredStub;
let loginGetChallengeStub;
beforeEach(function (done) {
notRequiredStub = h.stubGetChallenge({
notRequiredStub = h.stubGetSignupChallenge({
required: false
});
loginGetChallengeStub = h.stubGetChallenge([recaptchav2Response]);
this.lock = h.displayLock('', lockOpts, done);
});

Expand All @@ -169,6 +171,7 @@ describe('captcha on signup', function () {
});

it('should not show the captcha input', function () {
expect(loginGetChallengeStub.calledOnce).to.be.false;
expect(h.q(this.lock, '.auth0-lock-recaptchav2')).to.not.be.ok();
});

Expand All @@ -181,7 +184,7 @@ describe('captcha on signup', function () {
setTimeout(done, 260);
});

challengeStub = h.stubGetChallenge(recaptchav2Response);
challengeStub = h.stubGetSignupChallenge([recaptchav2Response]);
h.fillEmailInput(this.lock, '[email protected]');
h.fillComplexPassword(this.lock);
h.submitForm(this.lock);
Expand All @@ -190,6 +193,7 @@ describe('captcha on signup', function () {
it('should call the challenge api again and show the input', function () {
expect(notRequiredStub.calledOnce).to.be.true;
expect(challengeStub.calledOnce).to.be.true;
expect(loginGetChallengeStub.calledOnce).to.be.false;
expect(h.q(this.lock, '.auth0-lock-recaptchav2')).to.be.ok();
});
});
Expand Down
Loading

0 comments on commit 9198b14

Please sign in to comment.