🚀 feat: Add Cloudflare Turnstile support (#5987)

* 🚀 feat: Add @marsidev/react-turnstile dependency to package.json and package-lock.json

* 🚀 feat: Integrate Cloudflare Turnstile configuration support in AppService and add schema validation

* 🚀 feat: Implemented Cloudflare Turnstile integration in Login and Registration forms

* 🚀 feat: Enhance AppService tests with additional mocks and configuration setups

* 🚀 feat: Comment out outdated config version warning tests in AppService.spec.js

* 🚀 feat: Remove outdated warning tests and add new checks for environment variables and API health

* 🔧 test: Update AppService.spec.js to use expect.anything() for paths validation

* 🔧 test: Refactor AppService.spec.js to streamline mocks and enhance clarity

* 🔧 chore: removed not needed test

* Potential fix for code scanning alert no. 5638: Ensure code is properly formatted, use insertion, deletion, or replacement to obtain desired formatting.

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>

* Potential fix for code scanning alert no. 5629: Ensure code is properly formatted, use insertion, deletion, or replacement to obtain desired formatting.

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>

* Potential fix for code scanning alert no. 5642: Ensure code is properly formatted, use insertion, deletion, or replacement to obtain desired formatting.

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>

* Update turnstile.js

* Potential fix for code scanning alert no. 5634: Ensure code is properly formatted, use insertion, deletion, or replacement to obtain desired formatting.

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>

* Potential fix for code scanning alert no. 5646: Ensure code is properly formatted, use insertion, deletion, or replacement to obtain desired formatting.

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>

* Potential fix for code scanning alert no. 5647: Ensure code is properly formatted, use insertion, deletion, or replacement to obtain desired formatting.

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>

---------

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
This commit is contained in:
Ruben Talstra 2025-05-15 15:38:58 +02:00 committed by Danny Avila
parent 621fa6e1aa
commit 535e7798b3
10 changed files with 145 additions and 11 deletions

View file

@ -1,9 +1,10 @@
import { useForm } from 'react-hook-form';
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useContext } from 'react';
import { Turnstile } from '@marsidev/react-turnstile';
import type { TLoginUser, TStartupConfig } from 'librechat-data-provider';
import type { TAuthContext } from '~/common';
import { useResendVerificationEmail, useGetStartupConfig } from '~/data-provider';
import { useLocalize } from '~/hooks';
import { ThemeContext, useLocalize } from '~/hooks';
type TLoginFormProps = {
onSubmit: (data: TLoginUser) => void;
@ -14,6 +15,7 @@ type TLoginFormProps = {
const LoginForm: React.FC<TLoginFormProps> = ({ onSubmit, startupConfig, error, setError }) => {
const localize = useLocalize();
const { theme } = useContext(ThemeContext);
const {
register,
getValues,
@ -21,9 +23,11 @@ const LoginForm: React.FC<TLoginFormProps> = ({ onSubmit, startupConfig, error,
formState: { errors },
} = useForm<TLoginUser>();
const [showResendLink, setShowResendLink] = useState<boolean>(false);
const [turnstileToken, setTurnstileToken] = useState<string | null>(null);
const { data: config } = useGetStartupConfig();
const useUsernameLogin = config?.ldap?.username;
const validTheme = theme === 'dark' ? 'dark' : 'light';
useEffect(() => {
if (error && error.includes('422') && !showResendLink) {
@ -159,11 +163,29 @@ const LoginForm: React.FC<TLoginFormProps> = ({ onSubmit, startupConfig, error,
{localize('com_auth_password_forgot')}
</a>
)}
{/* Render Turnstile only if enabled in startupConfig */}
{startupConfig.turnstile && (
<div className="my-4 flex justify-center">
<Turnstile
siteKey={startupConfig.turnstile.siteKey}
options={{
...startupConfig.turnstile.options,
theme: validTheme,
}}
onSuccess={(token) => setTurnstileToken(token)}
onError={() => setTurnstileToken(null)}
onExpire={() => setTurnstileToken(null)}
/>
</div>
)}
<div className="mt-6">
<button
aria-label={localize('com_auth_continue')}
data-testid="login-button"
type="submit"
disabled={startupConfig.turnstile ? !turnstileToken : false}
className="
w-full rounded-2xl bg-green-600 px-4 py-3 text-sm font-medium text-white
transition-colors hover:bg-green-700 dark:bg-green-600 dark:hover:bg-green-700

View file

@ -1,16 +1,18 @@
import { useForm } from 'react-hook-form';
import React, { useState } from 'react';
import React, { useContext, useState } from 'react';
import { Turnstile } from '@marsidev/react-turnstile';
import { useNavigate, useOutletContext, useLocation } from 'react-router-dom';
import { useRegisterUserMutation } from 'librechat-data-provider/react-query';
import type { TRegisterUser, TError } from 'librechat-data-provider';
import type { TLoginLayoutContext } from '~/common';
import { ErrorMessage } from './ErrorMessage';
import { Spinner } from '~/components/svg';
import { useLocalize, TranslationKeys } from '~/hooks';
import { useLocalize, TranslationKeys, ThemeContext } from '~/hooks';
const Registration: React.FC = () => {
const navigate = useNavigate();
const localize = useLocalize();
const { theme } = useContext(ThemeContext);
const { startupConfig, startupConfigError, isFetching } = useOutletContext<TLoginLayoutContext>();
const {
@ -24,10 +26,12 @@ const Registration: React.FC = () => {
const [errorMessage, setErrorMessage] = useState<string>('');
const [isSubmitting, setIsSubmitting] = useState(false);
const [countdown, setCountdown] = useState<number>(3);
const [turnstileToken, setTurnstileToken] = useState<string | null>(null);
const location = useLocation();
const queryParams = new URLSearchParams(location.search);
const token = queryParams.get('token');
const validTheme = theme === 'dark' ? 'dark' : 'light';
const registerUser = useRegisterUserMutation({
onMutate: () => {
@ -178,17 +182,38 @@ const Registration: React.FC = () => {
validate: (value: string) =>
value === password || localize('com_auth_password_not_match'),
})}
{/* Render Turnstile only if enabled in startupConfig */}
{startupConfig?.turnstile && (
<div className="my-4 flex justify-center">
<Turnstile
siteKey={startupConfig.turnstile.siteKey}
options={{
...startupConfig.turnstile.options,
theme: validTheme,
}}
onSuccess={(token) => setTurnstileToken(token)}
onError={() => setTurnstileToken(null)}
onExpire={() => setTurnstileToken(null)}
/>
</div>
)}
<div className="mt-6">
<button
disabled={Object.keys(errors).length > 0}
disabled={
Object.keys(errors).length > 0 ||
isSubmitting ||
(startupConfig?.turnstile ? !turnstileToken : false)
}
type="submit"
aria-label="Submit registration"
className="
w-full rounded-2xl bg-green-600 px-4 py-3 text-sm font-medium text-white
transition-colors hover:bg-green-700 focus:outline-none focus:ring-2
focus:ring-green-500 focus:ring-offset-2 disabled:opacity-50
disabled:hover:bg-green-600 dark:bg-green-600 dark:hover:bg-green-700
"
w-full rounded-2xl bg-green-600 px-4 py-3 text-sm font-medium text-white
transition-colors hover:bg-green-700 focus:outline-none focus:ring-2
focus:ring-green-500 focus:ring-offset-2 disabled:opacity-50
disabled:hover:bg-green-600 dark:bg-green-600 dark:hover:bg-green-700
"
>
{isSubmitting ? <Spinner /> : localize('com_auth_continue')}
</button>