diff --git a/package-lock.json b/package-lock.json index d4a8daa..e7a254d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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": { diff --git a/package.json b/package.json index cc0478d..2212d32 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/components/profile/multiFactorAuth/MultiFactorAuth.intl.json b/src/components/profile/multiFactorAuth/MultiFactorAuth.intl.json index 8d6107c..9008bb3 100644 --- a/src/components/profile/multiFactorAuth/MultiFactorAuth.intl.json +++ b/src/components/profile/multiFactorAuth/MultiFactorAuth.intl.json @@ -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.", diff --git a/src/components/profile/multiFactorAuth/MultiFactorAuth.js b/src/components/profile/multiFactorAuth/MultiFactorAuth.js index 2989984..f971217 100644 --- a/src/components/profile/multiFactorAuth/MultiFactorAuth.js +++ b/src/components/profile/multiFactorAuth/MultiFactorAuth.js @@ -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 (
this.forceUpdate()} >
@@ -128,11 +136,16 @@ export default class MultiFactorAuth extends Component { } renderStepForms() { - const {activeStep} = this.state; + const {activeStep, secret, qrCodeSrc} = this.state; const steps = [ () => , - () => , + () => ( + + ), () => ( { - this.nextStep(); + 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); diff --git a/src/components/profile/multiFactorAuth/keyForm/KeyForm.js b/src/components/profile/multiFactorAuth/keyForm/KeyForm.js index 9c9a3ae..8630685 100644 --- a/src/components/profile/multiFactorAuth/keyForm/KeyForm.js +++ b/src/components/profile/multiFactorAuth/keyForm/KeyForm.js @@ -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 ( -
+

@@ -23,22 +26,22 @@ export default function KeyForm() {

- {key} + {secret}

-

+ -
+

- {key} + {formattedSecret}
@@ -50,3 +53,7 @@ export default function KeyForm() {
); } + +function formatSecret(secret: string): string { + return (secret.match(/.{1,4}/g) || []).join(' '); +} diff --git a/src/services/api/mfa.js b/src/services/api/mfa.js new file mode 100644 index 0000000..51735dc --- /dev/null +++ b/src/services/api/mfa.js @@ -0,0 +1,9 @@ +// @flow +import request from 'services/request'; +import type { Resp } from 'services/request'; + +export default { + getSecret(): Promise> { + return request.get('/api/two-factor-auth'); + } +}; diff --git a/src/services/request/index.js b/src/services/request/index.js index 72ac36a..e92740a 100644 --- a/src/services/request/index.js +++ b/src/services/request/index.js @@ -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 = { get: (key: T) => ?string, set: (key: T, value: any) => void, }; - -export default request; - -export { InternalServerError }; diff --git a/src/services/request/request.js b/src/services/request/request.js index bf732d2..72968f7 100644 --- a/src/services/request/request.js +++ b/src/services/request/request.js @@ -1,17 +1,28 @@ +// @flow import PromiseMiddlewareLayer from './PromiseMiddlewareLayer'; import InternalServerError from './InternalServerError'; const middlewareLayer = new PromiseMiddlewareLayer(); +export type Resp = { + 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(url: string, data?: Object, options: Object = {}): Promise> { 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(url: string, data?: Object, options: Object = {}): Promise> { 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) =>