mirror of
https://github.com/elyby/accounts-frontend.git
synced 2024-11-10 10:02:02 +05:30
#305: draft otp secret request integration
This commit is contained in:
parent
a8ae0e0c05
commit
ba8b725f9f
8
package-lock.json
generated
8
package-lock.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ely-by-account",
|
||||
"version": "1.1.19-dev",
|
||||
"version": "1.1.21-dev",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@ -3714,9 +3714,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"flow-bin": {
|
||||
"version": "0.47.0",
|
||||
"resolved": "https://registry.npmjs.org/flow-bin/-/flow-bin-0.47.0.tgz",
|
||||
"integrity": "sha1-oqCKs+DR8ctX0X4nswsRi2L9o2c=",
|
||||
"version": "0.51.1",
|
||||
"resolved": "https://registry.npmjs.org/flow-bin/-/flow-bin-0.51.1.tgz",
|
||||
"integrity": "sha1-eSnG8KlOdlQp/LLubkaCePqpxzI=",
|
||||
"dev": true
|
||||
},
|
||||
"fontgen-loader": {
|
||||
|
@ -72,7 +72,7 @@
|
||||
"exports-loader": "^0.6.3",
|
||||
"extract-text-webpack-plugin": "^1.0.0",
|
||||
"file-loader": "^0.11.0",
|
||||
"flow-bin": "^0.47.0",
|
||||
"flow-bin": "^0.51.1",
|
||||
"fontgen-loader": "^0.2.1",
|
||||
"html-loader": "^0.4.3",
|
||||
"html-webpack-plugin": "^2.0.0",
|
||||
|
@ -7,7 +7,7 @@
|
||||
"getAlternativeApps": "Get alternative apps",
|
||||
"theAppIsInstalled": "The app is installed",
|
||||
|
||||
"scanQrCode": "Open your favorit QR scanner app and scan the following QR code:",
|
||||
"scanQrCode": "Open your favorite QR scanner app and scan the following QR code:",
|
||||
"or": "OR",
|
||||
"enterKeyManually": "If you can't scan QR code, then enter the secret key manually:",
|
||||
"whenKeyEntered": "Go to the next step, after you will see temporary code in your two-factor auth app.",
|
||||
|
@ -10,6 +10,7 @@ import styles from 'components/profile/profileForm.scss';
|
||||
import helpLinks from 'components/auth/helpLinks.scss';
|
||||
import Stepper from 'components/ui/stepper';
|
||||
import { ScrollMotion } from 'components/ui/motion';
|
||||
import mfa from 'services/api/mfa';
|
||||
|
||||
import Instructions from './instructions';
|
||||
import KeyForm from './keyForm';
|
||||
@ -39,11 +40,17 @@ export default class MultiFactorAuth extends Component {
|
||||
};
|
||||
|
||||
state: {
|
||||
isLoading: bool,
|
||||
activeStep: number,
|
||||
secret: string,
|
||||
qrCodeSrc: string,
|
||||
code: string,
|
||||
newEmail: ?string
|
||||
} = {
|
||||
isLoading: false,
|
||||
activeStep: this.props.step,
|
||||
qrCodeSrc: '',
|
||||
secret: '',
|
||||
code: this.props.code || '',
|
||||
newEmail: null
|
||||
};
|
||||
@ -56,7 +63,7 @@ export default class MultiFactorAuth extends Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const {activeStep} = this.state;
|
||||
const {activeStep, isLoading} = this.state;
|
||||
const form = this.props.stepForm;
|
||||
|
||||
const stepsData = [
|
||||
@ -76,6 +83,7 @@ export default class MultiFactorAuth extends Component {
|
||||
return (
|
||||
<Form form={form}
|
||||
onSubmit={this.onFormSubmit}
|
||||
isLoading={isLoading}
|
||||
onInvalid={() => this.forceUpdate()}
|
||||
>
|
||||
<div className={styles.contentWithBackButton}>
|
||||
@ -128,11 +136,16 @@ export default class MultiFactorAuth extends Component {
|
||||
}
|
||||
|
||||
renderStepForms() {
|
||||
const {activeStep} = this.state;
|
||||
const {activeStep, secret, qrCodeSrc} = this.state;
|
||||
|
||||
const steps = [
|
||||
() => <Instructions key="step1" />,
|
||||
() => <KeyForm key="step2" />,
|
||||
() => (
|
||||
<KeyForm key="step2"
|
||||
secret={secret}
|
||||
qrCodeSrc={qrCodeSrc}
|
||||
/>
|
||||
),
|
||||
() => (
|
||||
<Confirmation key="step3"
|
||||
form={this.props.stepForm}
|
||||
@ -183,7 +196,15 @@ export default class MultiFactorAuth extends Component {
|
||||
};
|
||||
|
||||
onFormSubmit = () => {
|
||||
this.setState({isLoading: true});
|
||||
mfa.getSecret().then((resp) => {
|
||||
this.setState({
|
||||
isLoading: false,
|
||||
secret: resp.secret,
|
||||
qrCodeSrc: `data:image/svg+xml;base64,${resp.qr}`
|
||||
});
|
||||
this.nextStep();
|
||||
});
|
||||
// const {activeStep} = this.state;
|
||||
// const form = this.props.stepForms[activeStep];
|
||||
// const promise = this.props.onSubmit(activeStep, form);
|
||||
|
@ -10,11 +10,14 @@ import messages from '../MultiFactorAuth.intl.json';
|
||||
|
||||
import styles from './key-form.scss';
|
||||
|
||||
export default function KeyForm() {
|
||||
const key = '123 123 52354 1234';
|
||||
export default function KeyForm({secret, qrCodeSrc}: {
|
||||
secret: string,
|
||||
qrCodeSrc: string
|
||||
}) {
|
||||
const formattedSecret = formatSecret(secret);
|
||||
|
||||
return (
|
||||
<div className={profileForm.formBody} key="step2">
|
||||
<div className={profileForm.formBody}>
|
||||
<div className={profileForm.formRow}>
|
||||
<p className={profileForm.description}>
|
||||
<Message {...messages.scanQrCode} />
|
||||
@ -23,22 +26,22 @@ export default function KeyForm() {
|
||||
|
||||
<div className={profileForm.formRow}>
|
||||
<div className={styles.qrCode}>
|
||||
<img src="//placekitten.com/g/242/242" alt={key} />
|
||||
<img src={qrCodeSrc} alt={secret} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={profileForm.formRow}>
|
||||
<p className={classNames(styles.manualDescription, profileForm.description)}>
|
||||
<div className={styles.or}>
|
||||
<span className={styles.or}>
|
||||
<Message {...messages.or} />
|
||||
</div>
|
||||
</span>
|
||||
<Message {...messages.enterKeyManually} />
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className={profileForm.formRow}>
|
||||
<div className={styles.key}>
|
||||
{key}
|
||||
{formattedSecret}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -50,3 +53,7 @@ export default function KeyForm() {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function formatSecret(secret: string): string {
|
||||
return (secret.match(/.{1,4}/g) || []).join(' ');
|
||||
}
|
||||
|
9
src/services/api/mfa.js
Normal file
9
src/services/api/mfa.js
Normal file
@ -0,0 +1,9 @@
|
||||
// @flow
|
||||
import request from 'services/request';
|
||||
import type { Resp } from 'services/request';
|
||||
|
||||
export default {
|
||||
getSecret(): Promise<Resp<{qr: string, secret: string, uri: string}>> {
|
||||
return request.get('/api/two-factor-auth');
|
||||
}
|
||||
};
|
@ -1,5 +1,7 @@
|
||||
import request from './request';
|
||||
import InternalServerError from './InternalServerError';
|
||||
// @flow
|
||||
export { default } from './request';
|
||||
export type { Resp } from './request';
|
||||
export { default as InternalServerError } from './InternalServerError';
|
||||
|
||||
/**
|
||||
* Usage: Query<'requeired'|'keys'|'names'>
|
||||
@ -9,7 +11,3 @@ export type Query<T: string> = {
|
||||
get: (key: T) => ?string,
|
||||
set: (key: T, value: any) => void,
|
||||
};
|
||||
|
||||
export default request;
|
||||
|
||||
export { InternalServerError };
|
||||
|
@ -1,17 +1,28 @@
|
||||
// @flow
|
||||
import PromiseMiddlewareLayer from './PromiseMiddlewareLayer';
|
||||
import InternalServerError from './InternalServerError';
|
||||
|
||||
const middlewareLayer = new PromiseMiddlewareLayer();
|
||||
|
||||
export type Resp<T> = {
|
||||
originalResponse: Response
|
||||
} & T;
|
||||
|
||||
type Middleware = {
|
||||
before?: () => Promise<*>,
|
||||
after?: () => Promise<*>,
|
||||
catch?: () => Promise<*>
|
||||
};
|
||||
|
||||
export default {
|
||||
/**
|
||||
* @param {string} url
|
||||
* @param {object} data - request data
|
||||
* @param {object} options - additional options for fetch or middlewares
|
||||
* @param {object} [data] - request data
|
||||
* @param {object} [options] - additional options for fetch or middlewares
|
||||
*
|
||||
* @return {Promise}
|
||||
*/
|
||||
post(url, data, options = {}) {
|
||||
post<T>(url: string, data?: Object, options: Object = {}): Promise<Resp<T>> {
|
||||
return doFetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
@ -24,12 +35,12 @@ export default {
|
||||
|
||||
/**
|
||||
* @param {string} url
|
||||
* @param {object} data - request data
|
||||
* @param {object} options - additional options for fetch or middlewares
|
||||
* @param {object} [data] - request data
|
||||
* @param {object} [options] - additional options for fetch or middlewares
|
||||
*
|
||||
* @return {Promise}
|
||||
*/
|
||||
get(url, data, options = {}) {
|
||||
get<T>(url: string, data?: Object, options: Object = {}): Promise<Resp<T>> {
|
||||
if (typeof data === 'object' && Object.keys(data).length) {
|
||||
const separator = url.indexOf('?') === -1 ? '?' : '&';
|
||||
url += separator + buildQuery(data);
|
||||
@ -59,13 +70,15 @@ export default {
|
||||
* get response and callback to restart request as an arguments and should
|
||||
* return a Promise that resolves to the new response.
|
||||
*/
|
||||
addMiddleware(middleware) {
|
||||
addMiddleware(middleware: Middleware) {
|
||||
middlewareLayer.add(middleware);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const checkStatus = (resp) => Promise[resp.status >= 200 && resp.status < 300 ? 'resolve' : 'reject'](resp);
|
||||
const checkStatus = (resp) => resp.status >= 200 && resp.status < 300
|
||||
? Promise.resolve(resp)
|
||||
: Promise.reject(resp);
|
||||
const toJSON = (resp = {}) => {
|
||||
if (!resp.json) {
|
||||
// e.g. 'TypeError: Failed to fetch' due to CORS
|
||||
@ -87,7 +100,9 @@ const rejectWithJSON = (resp) => toJSON(resp).then((resp) => {
|
||||
|
||||
throw resp;
|
||||
});
|
||||
const handleResponseSuccess = (resp) => Promise[resp.success || typeof resp.success === 'undefined' ? 'resolve' : 'reject'](resp);
|
||||
const handleResponseSuccess = (resp) => resp.success || typeof resp.success === 'undefined'
|
||||
? Promise.resolve(resp)
|
||||
: Promise.reject(resp);
|
||||
|
||||
function doFetch(url, options = {}) {
|
||||
// NOTE: we are wrapping fetch, because it is returning
|
||||
@ -136,7 +151,7 @@ function convertQueryValue(value) {
|
||||
*
|
||||
* @return {string}
|
||||
*/
|
||||
function buildQuery(data = {}) {
|
||||
function buildQuery(data: Object = {}): string {
|
||||
return Object.keys(data)
|
||||
.map(
|
||||
(keyName) =>
|
||||
|
Loading…
Reference in New Issue
Block a user