mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-19 09:50:15 +01:00
🔒 feat: password reset disable option; fix: account email error message (#2327)
* feat: password reset disable option; fix: account email leak * fix(LoginSpec): typo * test: fixed LoginForm test * fix: disable password reset when undefined * refactor: use a helper function * fix: tests * feat: Remove unused error message in password reset process * chore: Update password reset email message * refactor: only allow password reset if explicitly allowed * feat: Add password reset email service configuration check The code changes in `checks.js` add a new function `checkPasswordReset()` that checks if the email service is configured when password reset is enabled. If the email service is not configured, a warning message is logged. This change ensures secure password reset functionality by prompting the user to configure the email service. Co-authored-by: Berry-13 <root@Berry> Co-authored-by: Danny Avila <messagedaniel@protonmail.com> Co-authored-by: Danny Avila <danny@librechat.ai> * chore: remove import order rules * refactor: simplify password reset logic and align against Observable Response Discrepancy * chore: make password reset warning more prominent * chore(AuthService): better logging for password resets, refactor requestPasswordReset to use req object, fix sendEmail error when email config is not present * refactor: fix styling of password reset email message * chore: add missing type for passwordResetEnabled, TStartupConfig * fix(LoginForm): prevent login form flickering * fix(ci): Update login form to use mocked startupConfig for rendering correctly * refactor: Improve password reset UI, applies DRY * chore: Add logging to password reset validation middleware * chore(CONTRIBUTING): Update import order conventions --------- Co-authored-by: Danny Avila <danny@librechat.ai> Co-authored-by: Berry-13 <root@Berry> Co-authored-by: Danny Avila <messagedaniel@protonmail.com>
This commit is contained in:
parent
a7f5b57272
commit
5452d4c20c
20 changed files with 288 additions and 137 deletions
|
|
@ -1,11 +1,37 @@
|
|||
import { useForm } from 'react-hook-form';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useState, ReactNode } from 'react';
|
||||
import { useOutletContext } from 'react-router-dom';
|
||||
import { useRequestPasswordResetMutation } from 'librechat-data-provider/react-query';
|
||||
import type { TRequestPasswordReset, TRequestPasswordResetResponse } from 'librechat-data-provider';
|
||||
import type { FC } from 'react';
|
||||
import type { TLoginLayoutContext } from '~/common';
|
||||
import { useLocalize } from '~/hooks';
|
||||
|
||||
const BodyTextWrapper: FC<{ children: ReactNode }> = ({ children }) => {
|
||||
return (
|
||||
<div
|
||||
className="relative mt-4 rounded border border-green-400 bg-green-100 px-4 py-3 text-green-700 dark:bg-green-900 dark:text-white"
|
||||
role="alert"
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const ResetPasswordBodyText = () => {
|
||||
const localize = useLocalize();
|
||||
return (
|
||||
<div className="flex flex-col">
|
||||
{localize('com_auth_reset_password_if_email_exists')}
|
||||
<span>
|
||||
<a className="text-sm text-green-500 hover:underline" href="/login">
|
||||
{localize('com_auth_back_to_login')}
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
function RequestPasswordReset() {
|
||||
const localize = useLocalize();
|
||||
const {
|
||||
|
|
@ -13,72 +39,39 @@ function RequestPasswordReset() {
|
|||
handleSubmit,
|
||||
formState: { errors },
|
||||
} = useForm<TRequestPasswordReset>();
|
||||
const [resetLink, setResetLink] = useState<string | undefined>(undefined);
|
||||
const [bodyText, setBodyText] = useState<React.ReactNode | undefined>(undefined);
|
||||
const { startupConfig, setError, setHeaderText } = useOutletContext<TLoginLayoutContext>();
|
||||
const [bodyText, setBodyText] = useState<ReactNode | undefined>(undefined);
|
||||
const { startupConfig, setHeaderText } = useOutletContext<TLoginLayoutContext>();
|
||||
|
||||
const requestPasswordReset = useRequestPasswordResetMutation();
|
||||
|
||||
const onSubmit = (data: TRequestPasswordReset) => {
|
||||
requestPasswordReset.mutate(data, {
|
||||
onSuccess: (data: TRequestPasswordResetResponse) => {
|
||||
if (!startupConfig?.emailEnabled) {
|
||||
setResetLink(data.link);
|
||||
if (data.link && !startupConfig?.emailEnabled) {
|
||||
setHeaderText('com_auth_reset_password');
|
||||
setBodyText(
|
||||
<span>
|
||||
{localize('com_auth_click')}{' '}
|
||||
<a className="text-green-500 hover:underline" href={data.link}>
|
||||
{localize('com_auth_here')}
|
||||
</a>{' '}
|
||||
{localize('com_auth_to_reset_your_password')}
|
||||
</span>,
|
||||
);
|
||||
} else {
|
||||
setHeaderText('com_auth_reset_password_link_sent');
|
||||
setBodyText(<ResetPasswordBodyText />);
|
||||
}
|
||||
},
|
||||
onError: () => {
|
||||
setError('com_auth_error_reset_password');
|
||||
setTimeout(() => {
|
||||
setError(null);
|
||||
}, 5000);
|
||||
setHeaderText('com_auth_reset_password_link_sent');
|
||||
setBodyText(<ResetPasswordBodyText />);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (bodyText) {
|
||||
return;
|
||||
}
|
||||
if (!requestPasswordReset.isSuccess) {
|
||||
setHeaderText('com_auth_reset_password');
|
||||
setBodyText(undefined);
|
||||
return;
|
||||
}
|
||||
|
||||
if (startupConfig?.emailEnabled) {
|
||||
setHeaderText('com_auth_reset_password_link_sent');
|
||||
setBodyText(localize('com_auth_reset_password_email_sent'));
|
||||
return;
|
||||
}
|
||||
|
||||
setHeaderText('com_auth_reset_password');
|
||||
setBodyText(
|
||||
<span>
|
||||
{localize('com_auth_click')}{' '}
|
||||
<a className="text-green-500 hover:underline" href={resetLink}>
|
||||
{localize('com_auth_here')}
|
||||
</a>{' '}
|
||||
{localize('com_auth_to_reset_your_password')}
|
||||
</span>,
|
||||
);
|
||||
}, [
|
||||
requestPasswordReset.isSuccess,
|
||||
startupConfig?.emailEnabled,
|
||||
resetLink,
|
||||
localize,
|
||||
setHeaderText,
|
||||
bodyText,
|
||||
]);
|
||||
|
||||
if (bodyText) {
|
||||
return (
|
||||
<div
|
||||
className="relative mt-4 rounded border border-green-400 bg-green-100 px-4 py-3 text-green-700 dark:bg-green-900 dark:text-white"
|
||||
role="alert"
|
||||
>
|
||||
{bodyText}
|
||||
</div>
|
||||
);
|
||||
return <BodyTextWrapper>{bodyText}</BodyTextWrapper>;
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue