diff --git a/apps/api/src/auth/auth.service.ts b/apps/api/src/auth/auth.service.ts
index a84d4ee..ba7245a 100644
--- a/apps/api/src/auth/auth.service.ts
+++ b/apps/api/src/auth/auth.service.ts
@@ -358,11 +358,13 @@ export class AuthService {
if (code.code) {
await this.mailerService.sendEmail(
user,
- 'Email verification',
- HandlebarsTemplate.EMAIL_CONFIRMATION,
+ 'Two Factor Authentication Recovery',
+ HandlebarsTemplate.CONFIRM_RECOVERY,
{
name: user.firstName,
- code: code.code
+ code: code.code,
+ email: user.email,
+ baseUrl: process.env.PLATFORM_URL
}
)
return user
diff --git a/apps/api/src/mailer/templates/confirm-2fa-recovery.hbs b/apps/api/src/mailer/templates/confirm-2fa-recovery.hbs
new file mode 100644
index 0000000..cd6da73
--- /dev/null
+++ b/apps/api/src/mailer/templates/confirm-2fa-recovery.hbs
@@ -0,0 +1,14 @@
+
+
+ 2FA Recovery
+
+
+ Two-Factor Authentication Recovery
+ Hello, {{name}}
+ To recover your account, please click on the link below:
+ Recover Account
+ If you did not request this, please ignore this email.
+
+
diff --git a/apps/api/src/mailer/types/mailer.types.ts b/apps/api/src/mailer/types/mailer.types.ts
index 89e7c69..127efbb 100644
--- a/apps/api/src/mailer/types/mailer.types.ts
+++ b/apps/api/src/mailer/types/mailer.types.ts
@@ -1,5 +1,6 @@
export enum HandlebarsTemplate {
PASSWORD_RESET_CODE = 'password-reset-code',
WELCOME = 'welcome',
- EMAIL_CONFIRMATION = 'email-confirmation'
+ EMAIL_CONFIRMATION = 'email-confirmation',
+ CONFIRM_RECOVERY = 'confirm-2fa-recovery'
}
diff --git a/apps/platform/src/hooks/use2FA.ts b/apps/platform/src/hooks/use2FA.ts
index 9812636..c3956e0 100644
--- a/apps/platform/src/hooks/use2FA.ts
+++ b/apps/platform/src/hooks/use2FA.ts
@@ -4,7 +4,9 @@ import {
Verify2FAData,
turnOn2FAServiceStep1,
turnOn2FAStep2Service,
- auth2FAService
+ auth2FAService,
+ authDisableService,
+ Recover2FAData
} from '@isomera/impl'
import useSession from './useSession'
@@ -32,11 +34,23 @@ const useTwoFactorAuthHook = () => {
return response
})
+ const { mutateAsync: disable2FA, isLoading: isLoadingDisable } = useMutation(
+ async (data: Pure) => {
+ const response = await authDisableService(data)
+ return response
+ }
+ )
+
return {
generate2FA,
verify2FA,
authenticate,
- isLoading: isLoadingGenerate || isLoadingVerify || isLoadingAuthenticate
+ disable2FA,
+ isLoading:
+ isLoadingGenerate ||
+ isLoadingVerify ||
+ isLoadingAuthenticate ||
+ isLoadingDisable
}
}
diff --git a/apps/platform/src/router/router.tsx b/apps/platform/src/router/router.tsx
index 9dac6e5..d4d8af0 100644
--- a/apps/platform/src/router/router.tsx
+++ b/apps/platform/src/router/router.tsx
@@ -16,6 +16,7 @@ import { PrivateLayout } from '../layouts/private.layout'
import { UserSecurityView } from '../views/user /profile-security.view'
import { Verify2FAView } from '../views/auth/auth2FA.view'
import { Recovery2FAView } from '../views/auth/recovery.view'
+import { Disable2FAView } from '../views/auth/disable-2fa.view'
function Router() {
return (
@@ -98,7 +99,7 @@ function Router() {
-
+
I don't have access to my 2fa and need to reset with
recovery code
@@ -108,7 +109,7 @@ function Router() {
/>
@@ -118,6 +119,17 @@ function Router() {
}
/>
+
+
+
+
+
+ }
+ />
+
{
+ const navigate = useNavigate()
+ const [searchParams] = useSearchParams()
+ const [message, setMessage] = useState('')
+
+ const { disable2FA, isLoading } = useTwoFactorAuthHook()
+
+ useEffect(() => {
+ const email = searchParams.get('email')
+ const code = searchParams.get('code')
+
+ if (!email || !code) {
+ navigate('/login')
+ } else {
+ disable2FA({ email, code })
+ .then(() => {
+ const msg =
+ 'Your 2fa was disabled, please log-in and enable 2fa again.'
+ toast.success(msg)
+ setMessage(msg)
+ setTimeout(() => navigate('/login'), 3000)
+ })
+ .catch((error: any) => {
+ console.error('Error disabling 2FA:', error)
+ navigate('/login')
+ })
+ }
+ }, [disable2FA, navigate, searchParams])
+
+ return (
+
+ {isLoading ?
{message}
:
Disabling 2FA, please wait...
}
+
+ )
+}
diff --git a/apps/platform/src/views/auth/recovery.view.tsx b/apps/platform/src/views/auth/recovery.view.tsx
index b9daa3c..0a2d241 100644
--- a/apps/platform/src/views/auth/recovery.view.tsx
+++ b/apps/platform/src/views/auth/recovery.view.tsx
@@ -2,6 +2,7 @@ import { useNavigate } from 'react-router-dom'
import useSession from '../../hooks/useSession'
import { pages, useHandleErrorHook } from '@isomera/impl'
import { useRecoveryHook } from '../../hooks/Recover2FAHook'
+import { toast } from 'react-toastify'
export const Recovery2FAView = () => {
const { loginWith2FA, user } = useSession()
@@ -10,6 +11,7 @@ export const Recovery2FAView = () => {
const onSuccess = (data: { access_token: string; refresh_token: string }) => {
loginWith2FA(data.access_token, data.refresh_token)
+ toast.success('Check your email for further recovery steps.')
navigate(pages.dashboard.path)
}
diff --git a/libs/impl/src/constants/apiRoutes.ts b/libs/impl/src/constants/apiRoutes.ts
index 3357cbd..665f3f2 100644
--- a/libs/impl/src/constants/apiRoutes.ts
+++ b/libs/impl/src/constants/apiRoutes.ts
@@ -23,6 +23,7 @@ export const API_AUTH_2FA_STEP_1 = 'auth/2fa/generate'
export const API_AUTH_2FA_STEP_2 = 'auth/2fa/turn-on'
export const API_AUTH_2FA_STEP_3 = 'auth/2fa/authenticate'
export const API_AUTH_2FA_RECOVER = 'auth/2fa/request-recovery'
+export const API_AUTH_2FA_CONFIRM_RECOVERY = 'auth/2fa/confirm-recovery'
// User
diff --git a/libs/impl/src/constants/pages.ts b/libs/impl/src/constants/pages.ts
index a96c6dd..11577c6 100644
--- a/libs/impl/src/constants/pages.ts
+++ b/libs/impl/src/constants/pages.ts
@@ -3,10 +3,13 @@ export const pages = {
path: '/login'
},
twoFA: {
- path: '/two-fa'
+ path: '/2fa'
},
- recoverTwoFA: {
- path: '/two-fa/recover'
+ recover2FA: {
+ path: '/2fa/recover'
+ },
+ confirmRecovery2FA: {
+ path: '/2fa/confirm-recovery'
},
register: {
path: '/sign-up'
diff --git a/libs/impl/src/services/auth/twoFactor.service.ts b/libs/impl/src/services/auth/twoFactor.service.ts
index 4d3e388..94461aa 100644
--- a/libs/impl/src/services/auth/twoFactor.service.ts
+++ b/libs/impl/src/services/auth/twoFactor.service.ts
@@ -1,5 +1,6 @@
import { UserInterface } from '@isomera/interfaces'
import {
+ API_AUTH_2FA_CONFIRM_RECOVERY,
API_AUTH_2FA_RECOVER,
API_AUTH_2FA_STEP_1,
API_AUTH_2FA_STEP_2,
@@ -65,3 +66,11 @@ export const auth2FARecoveryService = async (
return response.data
}
+
+export const authDisableService = async (
+ data: Verify2FAData
+): Promise => {
+ const response = await axiosInstance.post(API_AUTH_2FA_CONFIRM_RECOVERY, data)
+
+ return response.data
+}