Merge pull request #3276 from picklesrus/captcha-join

Initial work for captcha in new join flow.
This commit is contained in:
picklesrus 2019-08-29 16:10:50 -04:00 committed by GitHub
commit 73c353ba2c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 74 additions and 4 deletions

View file

@ -33,6 +33,11 @@ env:
- CLOUDDATA_HOST_VAR=CLOUDDATA_HOST_$TRAVIS_BRANCH - CLOUDDATA_HOST_VAR=CLOUDDATA_HOST_$TRAVIS_BRANCH
- CLOUDDATA_HOST=${!CLOUDDATA_HOST_VAR} - CLOUDDATA_HOST=${!CLOUDDATA_HOST_VAR}
- CLOUDDATA_HOST=${CLOUDDATA_HOST:-$CLOUDDATA_HOST_STAGING} - CLOUDDATA_HOST=${CLOUDDATA_HOST:-$CLOUDDATA_HOST_STAGING}
- RECAPTCHA_SITE_KEY_master=6LeRbUwUAAAAAFYhKgk3G9OKWqE_OJ7Z-7VTUCbl
- RECAPTCHA_SITE_KEY_STAGING=6LfukK4UAAAAAFR44yoZMhv8fj6xh-PMiIxwryG3
- RECAPTCHA_SITE=RECAPTCHA_SITE_KEY_$TRAVIS_BRANCH
- RECAPTCHA_SITE_KEY=${!RECAPTCHA_SITE_KEY_VAR}
- RECAPTCHA_SITE_KEY=${RECAPTCHA_SITE_KEY:-$RECAPTCHA_SITE_KEY_STAGING}
- ROOT_URL_master=https://scratch.mit.edu - ROOT_URL_master=https://scratch.mit.edu
- ROOT_URL_STAGING=https://scratch.ly - ROOT_URL_STAGING=https://scratch.ly
- ROOT_URL_VAR=ROOT_URL_$TRAVIS_BRANCH - ROOT_URL_VAR=ROOT_URL_$TRAVIS_BRANCH

View file

@ -20,16 +20,59 @@ class EmailStep extends React.Component {
'handleSetEmailRef', 'handleSetEmailRef',
'handleValidSubmit', 'handleValidSubmit',
'validateEmail', 'validateEmail',
'validateForm' 'validateForm',
'setCaptchaRef',
'captchaSolved',
'onCaptchaLoad',
'onCaptchaError'
]); ]);
this.state = {
captchaIsLoading: true
};
} }
componentDidMount () { componentDidMount () {
// automatically start with focus on username field // automatically start with focus on username field
if (this.emailInput) this.emailInput.focus(); if (this.emailInput) this.emailInput.focus();
// If grecaptcha doesn't exist on window, we havent loaded the captcha js yet. Load it.
if (!window.grecaptcha) {
// ReCaptcha calls a callback when the grecatpcha object is usable. That callback
// needs to be global so set it on the window.
window.grecaptchaOnLoad = this.onCaptchaLoad;
// Load Google ReCaptcha script.
const script = document.createElement('script');
script.async = true;
script.onerror = this.onCaptchaError;
script.src = `https://www.recaptcha.net/recaptcha/api.js?onload=grecaptchaOnLoad&render=explicit&hl=${window._locale}`;
document.body.appendChild(script);
}
}
componentWillUnmount () {
window.grecaptchaOnLoad = null;
} }
handleSetEmailRef (emailInputRef) { handleSetEmailRef (emailInputRef) {
this.emailInput = emailInputRef; this.emailInput = emailInputRef;
} }
onCaptchaError () {
// TODO send user to error step once we have one.
}
onCaptchaLoad () {
this.setState({captchaIsLoading: false});
this.grecaptcha = window.grecaptcha;
if (!this.grecaptcha) {
// According to the reCaptcha documentation, this callback shouldn't get
// called unless window.grecaptcha exists. This is just here to be extra defensive.
// TODO: Put up the error screen when we have one.
}
// TODO: Add in error callback for render once we have an error screen.
this.widgetId = this.grecaptcha.render(this.captchaRef,
{
callback: this.captchaSolved,
sitekey: process.env.RECAPTCHA_SITE_KEY
},
true);
}
validateEmail (email) { validateEmail (email) {
if (!email) return this.props.intl.formatMessage({id: 'general.required'}); if (!email) return this.props.intl.formatMessage({id: 'general.required'});
const isValidLocally = emailValidator.validate(email); const isValidLocally = emailValidator.validate(email);
@ -42,8 +85,21 @@ class EmailStep extends React.Component {
return {}; return {};
} }
handleValidSubmit (formData, formikBag) { handleValidSubmit (formData, formikBag) {
formikBag.setSubmitting(false); this.formData = formData;
this.props.onNextStep(formData); this.formikBag = formikBag;
// Change set submitting to false so that if the user clicks out of
// the captcha, the button is clickable again (instead of a disabled button with a spinner).
this.formikBag.setSubmitting(false);
this.grecaptcha.execute(this.widgetId);
}
captchaSolved (token) {
// Now thatcaptcha is done, we can tell Formik we're submitting.
this.formikBag.setSubmitting(true);
this.formData['g-recaptcha-response'] = token;
this.props.onNextStep(this.formData);
}
setCaptchaRef (ref) {
this.captchaRef = ref;
} }
render () { render () {
return ( return (
@ -88,7 +144,7 @@ class EmailStep extends React.Component {
innerClassName="join-flow-inner-email-step" innerClassName="join-flow-inner-email-step"
nextButton={this.props.intl.formatMessage({id: 'registration.createAccount'})} nextButton={this.props.intl.formatMessage({id: 'registration.createAccount'})}
title={this.props.intl.formatMessage({id: 'registration.emailStepTitle'})} title={this.props.intl.formatMessage({id: 'registration.emailStepTitle'})}
waiting={isSubmitting} waiting={isSubmitting || this.state.captchaIsLoading}
onSubmit={handleSubmit} onSubmit={handleSubmit}
> >
<FormikInput <FormikInput
@ -116,6 +172,13 @@ class EmailStep extends React.Component {
name="subscribe" name="subscribe"
/> />
</div> </div>
<div
className="g-recaptcha"
data-badge="bottomright"
data-sitekey={process.env.RECAPTCHA_SITE_KEY}
data-size="invisible"
ref={this.setCaptchaRef}
/>
</JoinFlowStep> </JoinFlowStep>
); );
}} }}

View file

@ -173,6 +173,8 @@ module.exports = {
new webpack.DefinePlugin({ new webpack.DefinePlugin({
'process.env.NODE_ENV': '"' + (process.env.NODE_ENV || 'development') + '"', 'process.env.NODE_ENV': '"' + (process.env.NODE_ENV || 'development') + '"',
'process.env.API_HOST': '"' + (process.env.API_HOST || 'https://api.scratch.mit.edu') + '"', 'process.env.API_HOST': '"' + (process.env.API_HOST || 'https://api.scratch.mit.edu') + '"',
'process.env.RECAPTCHA_SITE_KEY': '"' +
(process.env.RECAPTCHA_SITE_KEY || '6Lf6kK4UAAAAABKTyvdSqgcSVASEnMrCquiAkjVW') + '"',
'process.env.ASSET_HOST': '"' + (process.env.ASSET_HOST || 'https://assets.scratch.mit.edu') + '"', 'process.env.ASSET_HOST': '"' + (process.env.ASSET_HOST || 'https://assets.scratch.mit.edu') + '"',
'process.env.BACKPACK_HOST': '"' + (process.env.BACKPACK_HOST || 'https://backpack.scratch.mit.edu') + '"', 'process.env.BACKPACK_HOST': '"' + (process.env.BACKPACK_HOST || 'https://backpack.scratch.mit.edu') + '"',
'process.env.CLOUDDATA_HOST': '"' + (process.env.CLOUDDATA_HOST || 'clouddata.scratch.mit.edu') + '"', 'process.env.CLOUDDATA_HOST': '"' + (process.env.CLOUDDATA_HOST || 'clouddata.scratch.mit.edu') + '"',