accounts-frontend/src/components/profile/multiFactorAuth/MultiFactorAuth.js

205 lines
5.8 KiB
JavaScript
Raw Normal View History

2017-07-22 21:27:38 +05:30
// @flow
import React, { Component } from 'react';
import { FormattedMessage as Message } from 'react-intl';
import Helmet from 'react-helmet';
import { Button, FormModel } from 'components/ui/form';
2017-07-22 21:27:38 +05:30
import { BackButton } from 'components/profile/ProfileForm';
import styles from 'components/profile/profileForm.scss';
import Stepper from 'components/ui/stepper';
import { ScrollMotion } from 'components/ui/motion';
import logger from 'services/logger';
import mfa from 'services/api/mfa';
2017-07-22 21:27:38 +05:30
import Instructions from './instructions';
import KeyForm from './keyForm';
import Confirmation from './confirmation';
import messages from './MultiFactorAuth.intl.json';
import type { Form } from 'components/ui/form';
2017-07-22 21:27:38 +05:30
const STEPS_TOTAL = 3;
export type MfaStep = 0|1|2;
2017-07-22 21:27:38 +05:30
type Props = {
onChangeStep: Function,
lang: string,
email: string,
confirmationForm: FormModel,
onSubmit: (form: FormModel, sendData: () => Promise<*>) => Promise<*>,
onComplete: Function,
step: MfaStep
2017-07-22 21:27:38 +05:30
};
2017-08-23 02:01:41 +05:30
export default class MultiFactorAuth extends Component<Props, {
isLoading: bool,
activeStep: MfaStep,
secret: string,
qrCodeSrc: string
}> {
2017-07-22 21:27:38 +05:30
static defaultProps = {
confirmationForm: new FormModel(),
2017-07-22 21:27:38 +05:30
step: 0
};
state: {
isLoading: bool,
activeStep: MfaStep,
secret: string,
qrCodeSrc: string
2017-07-22 21:27:38 +05:30
} = {
isLoading: false,
2017-07-22 21:27:38 +05:30
activeStep: this.props.step,
qrCodeSrc: '',
secret: ''
2017-07-22 21:27:38 +05:30
};
confirmationFormEl: ?Form;
componentWillMount() {
this.syncState(this.props);
}
2017-07-22 21:27:38 +05:30
componentWillReceiveProps(nextProps: Props) {
this.syncState(nextProps);
2017-07-22 21:27:38 +05:30
}
render() {
const {activeStep, isLoading} = this.state;
2017-07-22 21:27:38 +05:30
const stepsData = [
{
buttonLabel: messages.theAppIsInstalled,
buttonAction: () => this.nextStep()
2017-07-22 21:27:38 +05:30
},
{
buttonLabel: messages.ready,
buttonAction: () => this.nextStep()
2017-07-22 21:27:38 +05:30
},
{
buttonLabel: messages.enableTwoFactorAuth,
buttonAction: () => this.confirmationFormEl && this.confirmationFormEl.submit()
2017-07-22 21:27:38 +05:30
}
];
const {buttonLabel, buttonAction} = stepsData[activeStep];
2017-07-22 21:27:38 +05:30
return (
<div className={styles.contentWithBackButton}>
<BackButton />
<div className={styles.form}>
<div className={styles.formBody}>
<Message {...messages.mfaTitle}>
{(pageTitle) => (
<h3 className={styles.title}>
<Helmet title={pageTitle} />
{pageTitle}
</h3>
)}
</Message>
<div className={styles.formRow}>
<p className={styles.description}>
<Message {...messages.mfaDescription} />
</p>
2017-07-22 21:27:38 +05:30
</div>
</div>
</div>
2017-07-22 21:27:38 +05:30
<div className={styles.stepper}>
<Stepper totalSteps={STEPS_TOTAL} activeStep={activeStep} />
</div>
2017-07-22 21:27:38 +05:30
<div className={styles.form}>
{this.renderStepForms()}
2017-07-22 21:27:38 +05:30
<Button
color="green"
onClick={buttonAction}
loading={isLoading}
block
label={buttonLabel}
/>
2017-07-22 21:27:38 +05:30
</div>
</div>
2017-07-22 21:27:38 +05:30
);
}
renderStepForms() {
const {activeStep, secret, qrCodeSrc} = this.state;
2017-07-22 21:27:38 +05:30
return (
<ScrollMotion activeStep={activeStep}>
{[
<Instructions key="step1" />,
<KeyForm key="step2"
secret={secret}
qrCodeSrc={qrCodeSrc}
/>,
<Confirmation key="step3"
form={this.props.confirmationForm}
formRef={(el: Form) => this.confirmationFormEl = el}
onSubmit={this.onTotpSubmit}
onInvalid={() => this.forceUpdate()}
/>
]}
2017-07-22 21:27:38 +05:30
</ScrollMotion>
);
}
syncState(props: Props) {
if (props.step === 1) {
this.setState({isLoading: true});
mfa.getSecret().then((resp) => {
this.setState({
isLoading: false,
secret: resp.secret,
qrCodeSrc: resp.qr
});
});
}
this.setState({
activeStep: typeof props.step === 'number' ? props.step : this.state.activeStep
});
}
2017-07-22 21:27:38 +05:30
nextStep() {
const nextStep = this.state.activeStep + 1;
2017-07-22 21:27:38 +05:30
if (nextStep < STEPS_TOTAL) {
this.props.onChangeStep(nextStep);
} else {
this.props.onComplete();
2017-07-22 21:27:38 +05:30
}
}
onTotpSubmit = (form: FormModel): Promise<*> => {
this.setState({isLoading: true});
2017-07-22 21:27:38 +05:30
return this.props.onSubmit(
form,
() => {
const data = form.serialize();
2017-07-22 21:27:38 +05:30
return mfa.enable(data);
}
)
.catch((resp) => {
const {errors} = resp || {};
2017-07-22 21:27:38 +05:30
if (errors) {
return Promise.reject(errors);
}
2017-07-22 21:27:38 +05:30
logger.error('MFA: Unexpected form submit result', {
resp
});
})
.finally(() => this.setState({isLoading: false}));
2017-07-22 21:27:38 +05:30
};
}