import React, { useState, useEffect, useCallback, } from 'react'; import { useTranslation } from 'next-i18next'; import { useRouter } from 'next/router'; import ReactCardFlip from 'react-card-flip'; import { apiv3Post } from '~/client/util/apiv3-client'; import type { IExternalAccountLoginError } from '~/interfaces/errors/external-account-login-error'; import { LoginErrorCode } from '~/interfaces/errors/login-error'; import type { IErrorV3 } from '~/interfaces/errors/v3-error'; import { RegistrationMode } from '~/interfaces/registration-mode'; import { toArrayIfNot } from '~/utils/array-utils'; import { CompleteUserRegistration } from './CompleteUserRegistration'; import styles from './LoginForm.module.scss'; type LoginFormProps = { username?: string, name?: string, email?: string, isEmailAuthenticationEnabled: boolean, registrationMode: RegistrationMode, registrationWhitelist: string[], isPasswordResetEnabled: boolean, isLocalStrategySetup: boolean, isLdapStrategySetup: boolean, isLdapSetupFailed: boolean, objOfIsExternalAuthEnableds?: any, isMailerSetup?: boolean, externalAccountLoginError?: IExternalAccountLoginError, } export const LoginForm = (props: LoginFormProps): JSX.Element => { const { t } = useTranslation(); const router = useRouter(); const { isLocalStrategySetup, isLdapStrategySetup, isLdapSetupFailed, isPasswordResetEnabled, isEmailAuthenticationEnabled, registrationMode, registrationWhitelist, isMailerSetup, objOfIsExternalAuthEnableds, } = props; const isLocalOrLdapStrategiesEnabled = isLocalStrategySetup || isLdapStrategySetup; const isSomeExternalAuthEnabled = Object.values(objOfIsExternalAuthEnableds).some(elem => elem); // states const [isRegistering, setIsRegistering] = useState(false); const [isLoading, setIsLoading] = useState(false); // For Login const [usernameForLogin, setUsernameForLogin] = useState(''); const [passwordForLogin, setPasswordForLogin] = useState(''); const [loginErrors, setLoginErrors] = useState([]); // For Register const [usernameForRegister, setUsernameForRegister] = useState(''); const [nameForRegister, setNameForRegister] = useState(''); const [emailForRegister, setEmailForRegister] = useState(''); const [passwordForRegister, setPasswordForRegister] = useState(''); const [registerErrors, setRegisterErrors] = useState([]); // For UserActivation const [emailForRegistrationOrder, setEmailForRegistrationOrder] = useState(''); const [isSuccessToRagistration, setIsSuccessToRagistration] = useState(false); const isRegistrationEnabled = isLocalStrategySetup && registrationMode !== RegistrationMode.CLOSED; useEffect(() => { const { hash } = window.location; if (hash === '#register') { setIsRegistering(true); } }, []); const tWithOpt = useCallback((key: string, opt?: any): string => { if (typeof opt === 'object') { return t(key, opt as object); } return t(key); }, [t]); const handleLoginWithExternalAuth = useCallback((e) => { const auth = e.currentTarget.id; window.location.href = `/passport/${auth}`; }, []); const resetLoginErrors = useCallback(() => { if (loginErrors.length === 0) return; setLoginErrors([]); }, [loginErrors.length]); const handleLoginWithLocalSubmit = useCallback(async(e) => { e.preventDefault(); resetLoginErrors(); setIsLoading(true); const loginForm = { username: usernameForLogin, password: passwordForLogin, }; try { const res = await apiv3Post('/login', { loginForm }); const { redirectTo } = res.data; if (redirectTo != null) { return router.push(redirectTo); } return router.push('/'); } catch (err) { const errs = toArrayIfNot(err); setLoginErrors(errs); setIsLoading(false); } return; }, [passwordForLogin, resetLoginErrors, router, usernameForLogin]); // separate errors based on error code const separateErrorsBasedOnErrorCode = useCallback((errors: IErrorV3[]) => { const loginErrorListForDangerouslySetInnerHTML: IErrorV3[] = []; const loginErrorList: IErrorV3[] = []; errors.forEach((err) => { if (err.code === LoginErrorCode.PROVIDER_DUPLICATED_USERNAME_EXCEPTION) { loginErrorListForDangerouslySetInnerHTML.push(err); } else { loginErrorList.push(err); } }); return [loginErrorListForDangerouslySetInnerHTML, loginErrorList]; }, []); // wrap error elements which use dangerouslySetInnerHtml const generateDangerouslySetErrors = useCallback((errors: IErrorV3[]): JSX.Element => { if (errors == null || errors.length === 0) return <>; return (
{errors.map((err) => { // eslint-disable-next-line react/no-danger return ; })}
); }, [tWithOpt]); // wrap error elements which do not use dangerouslySetInnerHtml const generateSafelySetErrors = useCallback((errors: (IErrorV3 | IExternalAccountLoginError)[]): JSX.Element => { if (errors == null || errors.length === 0) return <>; return ( ); }, [tWithOpt]); const renderLocalOrLdapLoginForm = useCallback(() => { const { isLdapStrategySetup } = props; // separate login errors into two arrays based on error code const [loginErrorListForDangerouslySetInnerHTML, loginErrorList] = separateErrorsBasedOnErrorCode(loginErrors); // Generate login error elements using dangerouslySetInnerHTML const loginErrorElementWithDangerouslySetInnerHTML = generateDangerouslySetErrors(loginErrorListForDangerouslySetInnerHTML); // Generate login error elements using