From 795e045950718819921f0d7933939eaf7889bfda Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Thu, 2 Jul 2020 22:23:25 -0700 Subject: [PATCH] Display generated recovery tokens when enabling two factor --- .../api/account/enableAccountTwoFactor.ts | 10 +- .../dashboard/forms/SetupTwoFactorModal.tsx | 121 +++++++++++------- 2 files changed, 82 insertions(+), 49 deletions(-) diff --git a/resources/scripts/api/account/enableAccountTwoFactor.ts b/resources/scripts/api/account/enableAccountTwoFactor.ts index d44d09ac..e7a15f62 100644 --- a/resources/scripts/api/account/enableAccountTwoFactor.ts +++ b/resources/scripts/api/account/enableAccountTwoFactor.ts @@ -1,9 +1,7 @@ import http from '@/api/http'; -export default (code: string): Promise => { - return new Promise((resolve, reject) => { - http.post('/api/client/account/two-factor', { code }) - .then(() => resolve()) - .catch(reject); - }); +export default async (code: string): Promise => { + const { data } = await http.post('/api/client/account/two-factor', { code }); + + return data.attributes.tokens; }; diff --git a/resources/scripts/components/dashboard/forms/SetupTwoFactorModal.tsx b/resources/scripts/components/dashboard/forms/SetupTwoFactorModal.tsx index 028b2b36..ff5eeec7 100644 --- a/resources/scripts/components/dashboard/forms/SetupTwoFactorModal.tsx +++ b/resources/scripts/components/dashboard/forms/SetupTwoFactorModal.tsx @@ -2,21 +2,22 @@ import React, { useEffect, useState } from 'react'; import Modal, { RequiredModalProps } from '@/components/elements/Modal'; import { Form, Formik, FormikHelpers } from 'formik'; import { object, string } from 'yup'; -import Field from '@/components/elements/Field'; import getTwoFactorTokenUrl from '@/api/account/getTwoFactorTokenUrl'; import enableAccountTwoFactor from '@/api/account/enableAccountTwoFactor'; -import FlashMessageRender from '@/components/FlashMessageRender'; import { Actions, useStoreActions } from 'easy-peasy'; import { ApplicationStore } from '@/state'; import { httpErrorToHuman } from '@/api/http'; +import FlashMessageRender from '@/components/FlashMessageRender'; +import Field from '@/components/elements/Field'; interface Values { code: string; } -export default ({ ...props }: RequiredModalProps) => { +export default ({ onDismissed, ...props }: RequiredModalProps) => { const [ token, setToken ] = useState(''); const [ loading, setLoading ] = useState(true); + const [ recoveryTokens, setRecoveryTokens ] = useState([]); const updateUserData = useStoreActions((actions: Actions) => actions.user.updateUserData); const { addError, clearFlashes } = useStoreActions((actions: Actions) => actions.flashes); @@ -27,22 +28,30 @@ export default ({ ...props }: RequiredModalProps) => { .then(setToken) .catch(error => { console.error(error); + addError({ message: httpErrorToHuman(error), key: 'account:two-factor' }); }); }, []); const submit = ({ code }: Values, { setSubmitting }: FormikHelpers) => { clearFlashes('account:two-factor'); enableAccountTwoFactor(code) - .then(() => { - updateUserData({ useTotp: true }); - props.onDismissed(); + .then(tokens => { + setRecoveryTokens(tokens); }) .catch(error => { console.error(error); addError({ message: httpErrorToHuman(error), key: 'account:two-factor' }); - setSubmitting(false); - }); + }) + .then(() => setSubmitting(false)); + }; + + const dismiss = () => { + if (recoveryTokens.length > 0) { + updateUserData({ useTotp: true }); + } + + onDismissed(); }; return ( @@ -58,47 +67,73 @@ export default ({ ...props }: RequiredModalProps) => { {({ isSubmitting, isValid }) => ( -
- -
-
-
- {!token || !token.length ? - 0 ? + <> +

Two-factor authentication enabled

+

+ Two-factor authentication has been enabled on your account. Should you loose access to + this device you'll need to use on of the codes displayed below in order to access your + account. +

+

+ These codes will not be displayed again. Please take note of them now + by storing them in a secure repository such as a password manager. +

+
+                                {recoveryTokens.map(token => {token})}
+                            
+
+ +
+ + : + + +
+
+
+ {!token || !token.length ? + + : + setLoading(false)} + className={'w-full h-full shadow-none rounded-0'} + /> + } +
+
+
+
+ - : - setLoading(false)} - className={'w-full h-full shadow-none rounded-0'} - /> - } +
+
+ +
-
-
- -
-
- -
-
-
- + + } )}