Skip to content

Commit

Permalink
POC Reset Password Captcha
Browse files Browse the repository at this point in the history
  • Loading branch information
srijonsaha committed Mar 7, 2024
1 parent aaae03f commit b719e74
Show file tree
Hide file tree
Showing 10 changed files with 160 additions and 176 deletions.
249 changes: 89 additions & 160 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@
"webpack-dev-server": "^4.11.1"
},
"dependencies": {
"auth0-js": "^9.23.3",
"auth0-js": "file:../auth0.js",
"auth0-password-policies": "^1.0.2",
"blueimp-md5": "^2.19.0",
"classnames": "^2.3.2",
Expand Down
20 changes: 15 additions & 5 deletions src/connection/captcha.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import webApi from '../core/web_api';
* @param {Number} id
* @param {Boolean} isPasswordless Whether the captcha is being rendered in a passwordless flow
*/
export function showMissingCaptcha(m, id, isPasswordless = false) {
const captchaConfig = isPasswordless ? l.passwordlessCaptcha(m) : l.captcha(m);
export function showMissingCaptcha(m, id, isPasswordless = false, isPasswordReset = false) {
const captchaConfig = isPasswordReset ? l.resetPasswordCaptcha(m) : (isPasswordless ? l.passwordlessCaptcha(m) : l.captcha(m));

const captchaError = (
captchaConfig.get('provider') === 'recaptcha_v2' ||
Expand Down Expand Up @@ -42,8 +42,8 @@ export function showMissingCaptcha(m, id, isPasswordless = false) {
*
* @returns {Boolean} returns true if is required and missing the response from the user
*/
export function setCaptchaParams(m, params, isPasswordless, fields) {
const captchaConfig = isPasswordless ? l.passwordlessCaptcha(m) : l.captcha(m);
export function setCaptchaParams(m, params, isPasswordless, fields, isPasswordReset) {
const captchaConfig = isPasswordReset ? l.resetPasswordCaptcha(m) : (isPasswordless ? l.passwordlessCaptcha(m) : l.captcha(m));
const isCaptchaRequired = captchaConfig && captchaConfig.get('required');

if (!isCaptchaRequired) {
Expand All @@ -68,7 +68,17 @@ export function setCaptchaParams(m, params, isPasswordless, fields) {
* @param {boolean} wasInvalid A boolean indicating if the previous captcha was invalid.
* @param {Function} [next] A callback.
*/
export function swapCaptcha(id, isPasswordless, wasInvalid, next) {
export function swapCaptcha(id, isPasswordless, wasInvalid, next, isPasswordReset) {
if (isPasswordReset) {
return webApi.getResetPasswordChallenge(id, (err, newCaptcha) => {
if (!err && newCaptcha) {
swap(updateEntity, 'lock', id, l.setResetPasswordCaptcha, newCaptcha, wasInvalid);
}
if (next) {
next();
}
});
}
if (isPasswordless) {
return webApi.getPasswordlessChallenge(id, (err, newCaptcha) => {
if (!err && newCaptcha) {
Expand Down
5 changes: 5 additions & 0 deletions src/connection/database/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,11 @@ export function resetPassword(id) {
email: c.getFieldValue(m, 'email')
};

const isCaptchaValid = setCaptchaParams(m, params, false, [], true);
if (!isCaptchaValid) {
return showMissingCaptcha(m, id, false, true);
}

webApi.resetPassword(id, params, (error, ...args) => {
if (error) {
setTimeout(() => resetPasswordError(id, error), 250);
Expand Down
22 changes: 16 additions & 6 deletions src/connection/database/reset_password.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import * as l from '../../core/index';
import { swap, updateEntity } from '../../store/index';
import { isEmail, setEmail } from '../../field/email';
import { getField } from '../../field';
import CaptchaPane from '../../field/captcha/captcha_pane';

class Component extends React.Component {
componentDidMount() {
Expand All @@ -33,13 +34,22 @@ class Component extends React.Component {
const headerText = i18n.html('forgotPasswordInstructions') || null;
const header = headerText && <p>{headerText}</p>;

const captchaPane =
l.resetPasswordCaptcha(model) &&
l.resetPasswordCaptcha(model).get('required') ? (
<CaptchaPane i18n={i18n} lock={model} onReload={() => swapCaptcha(l.id(model), false, false, null, true)} />
) : null;

return (
<ResetPasswordPane
emailInputPlaceholder={i18n.str('emailInputPlaceholder')}
header={header}
i18n={i18n}
lock={model}
/>
<div>
<ResetPasswordPane
emailInputPlaceholder={i18n.str('emailInputPlaceholder')}
header={header}
i18n={i18n}
lock={model}
/>
{captchaPane}
</div>
);
}
}
Expand Down
11 changes: 10 additions & 1 deletion src/core/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,11 @@ export function setPasswordlessCaptcha(m, value, wasInvalid) {
return set(m, 'passwordlessCaptcha', Immutable.fromJS(value));
}

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

export function captcha(m) {
return get(m, 'captcha');
}
Expand All @@ -434,6 +439,10 @@ export function passwordlessCaptcha(m) {
return get(m, 'passwordlessCaptcha');
}

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

export function prefill(m) {
return get(m, 'prefill', {});
}
Expand Down Expand Up @@ -585,7 +594,7 @@ export function loginErrorMessage(m, error, type) {
currentCaptcha.get('provider') === 'recaptcha_enterprise' ||
currentCaptcha.get('provider') === 'hcaptcha' ||
currentCaptcha.get('provider') === 'auth0_v2' ||
captchaConfig.get('provider') === 'friendly_captcha'
currentCaptcha.get('provider') === 'friendly_captcha'
)) {
code = 'invalid_recaptcha';
}
Expand Down
11 changes: 10 additions & 1 deletion src/core/remote_data.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import * as l from './index';
import { isADEnabled } from '../connection/enterprise'; // shouldn't depend on this
import sync, { isSuccess } from '../sync';
import webApi from './web_api';
import { setCaptcha, setPasswordlessCaptcha } from '../core/index';
import { setCaptcha, setPasswordlessCaptcha, setResetPasswordCaptcha } from '../core/index';

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

m = sync(m, 'resetPasswordCaptcha', {
syncFn: (m, cb) => {
webApi.getResetPasswordChallenge(m.get('id'), (err, r) => {
cb(null, r);
});
},
successFn: setResetPasswordCaptcha
});


return m;
}
8 changes: 8 additions & 0 deletions src/core/web_api.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,14 @@ class Auth0WebAPI {
return this.clients[lockID].getPasswordlessChallenge(callback);
}

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

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

getSSOData(lockID, ...args) {
return this.clients[lockID].getSSOData(...args);
}
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 @@ -199,6 +199,10 @@ class Auth0APIClient {
return this.client.client.passwordless.getChallenge(...params);
}

getResetPasswordChallenge(...params) {
return this.client.client.dbConnection.getChallenge(...params);
}

getUserCountry(cb) {
return this.client.client.getUserCountry(cb);
}
Expand Down
4 changes: 2 additions & 2 deletions src/field/captcha/captcha_pane.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ import { ThirdPartyCaptcha, isThirdPartyCaptcha } from './third_party_captcha';

export default class CaptchaPane extends React.Component {
render() {
const { i18n, lock, onReload, isPasswordless } = this.props;
const { i18n, lock, onReload, isPasswordless, isPasswordReset } = this.props;
const lockId = l.id(lock);
const captcha = isPasswordless ? l.passwordlessCaptcha(lock) : l.captcha(lock);
const captcha = isPasswordReset ? l.resetPasswordCaptcha(lock) : (isPasswordless ? l.passwordlessCaptcha(lock) : l.captcha(lock));
const value = getFieldValue(lock, 'captcha');
const isValid = !isFieldVisiblyInvalid(lock, 'captcha');
const provider = captcha.get('provider');
Expand Down

0 comments on commit b719e74

Please sign in to comment.