diff --git a/src/frontend/src/components/forms/AuthenticationForm.tsx b/src/frontend/src/components/forms/AuthenticationForm.tsx index f3673700a8..f9cb05da52 100644 --- a/src/frontend/src/components/forms/AuthenticationForm.tsx +++ b/src/frontend/src/components/forms/AuthenticationForm.tsx @@ -1,3 +1,4 @@ +import { ApiEndpoints, apiUrl } from '@lib/index'; import { t } from '@lingui/core/macro'; import { Trans } from '@lingui/react/macro'; import { @@ -8,16 +9,14 @@ import { Loader, PasswordInput, Stack, - TextInput + TextInput, + VisuallyHidden } from '@mantine/core'; import { useForm } from '@mantine/form'; import { useDisclosure } from '@mantine/hooks'; +import { showNotification } from '@mantine/notifications'; import { useState } from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; - -import { ApiEndpoints } from '@lib/enums/ApiEndpoints'; -import { apiUrl } from '@lib/functions/Api'; -import { showNotification } from '@mantine/notifications'; import { useShallow } from 'zustand/react/shallow'; import { api } from '../../App'; import { @@ -33,7 +32,7 @@ import { SsoButton } from '../buttons/SSOButton'; export function AuthenticationForm() { const classicForm = useForm({ - initialValues: { username: '', password: '' } + initialValues: { username: '', password: '', code: '' } }); const simpleForm = useForm({ initialValues: { email: '' } }); const [classicLoginMode, setMode] = useDisclosure(true); @@ -58,7 +57,9 @@ export function AuthenticationForm() { doBasicLogin( classicForm.values.username, classicForm.values.password, - navigate + + navigate, + classicForm.values.code ) .then((success) => { setIsLoggingIn(false); @@ -140,6 +141,13 @@ export function AuthenticationForm() { placeholder={t`Your password`} {...classicForm.getInputProps('password')} /> + + {password_forgotten_enabled() === true && ( { + navigate: NavigateFunction, + code?: string +) { const { getHost } = useLocalState.getState(); const { clearUserState, setAuthenticated, fetchUserState } = useUserState.getState(); @@ -104,16 +105,9 @@ export const doBasicLogin = async ( success = true; } }) - .catch((err) => { + .catch(async (err) => { if (err?.response?.status == 401) { - setAuthContext(err.response.data?.data); - const mfa_flow = err.response.data.data.flows.find( - (flow: any) => flow.id == FlowEnum.MfaAuthenticate - ); - if (mfa_flow && mfa_flow.is_pending == true) { - success = true; - navigate('/mfa'); - } + await handlePossibleMFAError(err); } else if (err?.response?.status == 409) { notifications.show({ title: t`Already logged in`, @@ -133,7 +127,40 @@ export const doBasicLogin = async ( clearUserState(); } return success; -}; + + async function handlePossibleMFAError(err: any) { + setAuthContext(err.response.data?.data); + const mfa_flow = err.response.data.data.flows.find( + (flow: any) => flow.id == FlowEnum.MfaAuthenticate + ); + if (mfa_flow?.is_pending) { + // MFA is required - we might already have a code + if (code && code.length > 0) { + const rslt = await handleMfaLogin( + navigate, + undefined, + { code: code }, + () => {} + ); + if (rslt) { + setAuthenticated(true); + loginDone = true; + success = true; + notifications.show({ + title: t`MFA Login successful`, + message: t`MFA details were automatically provided in the browser`, + color: 'green' + }); + } + } + // No code or success - off to the mfa page + if (!loginDone) { + success = true; + navigate('/mfa'); + } + } + } +} /** * Logout the user from the current session @@ -259,19 +286,25 @@ export function handleReset( }); } -export function handleMfaLogin( +export async function handleMfaLogin( navigate: NavigateFunction, - location: Location, + location: Location | undefined, values: { code: string; remember?: boolean }, setError: (message: string | undefined) => void ) { const { setAuthContext } = useServerApiState.getState(); - authApi(apiUrl(ApiEndpoints.auth_login_2fa), undefined, 'post', { - code: values.code - }) + const result = await authApi( + apiUrl(ApiEndpoints.auth_login_2fa), + undefined, + 'post', + { + code: values.code + } + ) .then((response) => { handleSuccessFullAuth(response, navigate, location, setError); + return true; }) .catch((err) => { // Already logged in, but with a different session @@ -304,7 +337,9 @@ export function handleMfaLogin( } setError(msg); } + return false; }); + return result; } /** @@ -380,7 +415,7 @@ function handleSuccessFullAuth( observeProfile(); fetchGlobalStates(navigate); - if (navigate) { + if (navigate && location) { followRedirect(navigate, location?.state); } });