chore: handled the auto form submit for all authenticators (#5139)
This commit is contained in:
parent
6ade86f89d
commit
cd85a9fe09
2 changed files with 47 additions and 15 deletions
|
|
@ -1,6 +1,6 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import React, { useEffect, useMemo, useState } from "react";
|
import React, { useEffect, useMemo, useRef, useState } from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { Eye, EyeOff, XCircle } from "lucide-react";
|
import { Eye, EyeOff, XCircle } from "lucide-react";
|
||||||
import { Button, Input, Spinner } from "@plane/ui";
|
import { Button, Input, Spinner } from "@plane/ui";
|
||||||
|
|
@ -39,8 +39,10 @@ const authService = new AuthService();
|
||||||
|
|
||||||
export const AuthPasswordForm: React.FC<Props> = observer((props: Props) => {
|
export const AuthPasswordForm: React.FC<Props> = observer((props: Props) => {
|
||||||
const { email, nextPath, isSMTPConfigured, handleAuthStep, handleEmailClear, mode } = props;
|
const { email, nextPath, isSMTPConfigured, handleAuthStep, handleEmailClear, mode } = props;
|
||||||
|
// ref
|
||||||
|
const formRef = useRef<HTMLFormElement>(null);
|
||||||
// states
|
// states
|
||||||
const [csrfToken, setCsrfToken] = useState<string | undefined>(undefined);
|
const [csrfPromise, setCsrfPromise] = useState<Promise<{ csrf_token: string }> | undefined>(undefined);
|
||||||
const [passwordFormData, setPasswordFormData] = useState<TPasswordFormValues>({ ...defaultValues, email });
|
const [passwordFormData, setPasswordFormData] = useState<TPasswordFormValues>({ ...defaultValues, email });
|
||||||
const [showPassword, setShowPassword] = useState({
|
const [showPassword, setShowPassword] = useState({
|
||||||
password: false,
|
password: false,
|
||||||
|
|
@ -57,9 +59,11 @@ export const AuthPasswordForm: React.FC<Props> = observer((props: Props) => {
|
||||||
setPasswordFormData((prev) => ({ ...prev, [key]: value }));
|
setPasswordFormData((prev) => ({ ...prev, [key]: value }));
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (csrfToken === undefined)
|
if (csrfPromise === undefined) {
|
||||||
authService.requestCSRFToken().then((data) => data?.csrf_token && setCsrfToken(data.csrf_token));
|
const promise = authService.requestCSRFToken();
|
||||||
}, [csrfToken]);
|
setCsrfPromise(promise);
|
||||||
|
}
|
||||||
|
}, [csrfPromise]);
|
||||||
|
|
||||||
const redirectToUniqueCodeSignIn = async () => {
|
const redirectToUniqueCodeSignIn = async () => {
|
||||||
handleAuthStep(EAuthSteps.UNIQUE_CODE);
|
handleAuthStep(EAuthSteps.UNIQUE_CODE);
|
||||||
|
|
@ -88,15 +92,29 @@ export const AuthPasswordForm: React.FC<Props> = observer((props: Props) => {
|
||||||
const confirmPassword = passwordFormData.confirm_password ?? "";
|
const confirmPassword = passwordFormData.confirm_password ?? "";
|
||||||
const renderPasswordMatchError = !isRetryPasswordInputFocused || confirmPassword.length >= password.length;
|
const renderPasswordMatchError = !isRetryPasswordInputFocused || confirmPassword.length >= password.length;
|
||||||
|
|
||||||
|
const handleCSRFToken = async () => {
|
||||||
|
if (!formRef || !formRef.current) return;
|
||||||
|
const token = await csrfPromise;
|
||||||
|
if (!token?.csrf_token) return;
|
||||||
|
const csrfElement = formRef.current.querySelector("input[name=csrfmiddlewaretoken]");
|
||||||
|
csrfElement?.setAttribute("value", token?.csrf_token);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
|
ref={formRef}
|
||||||
className="mt-5 space-y-4"
|
className="mt-5 space-y-4"
|
||||||
method="POST"
|
method="POST"
|
||||||
action={`${API_BASE_URL}/auth/spaces/${mode === EAuthModes.SIGN_IN ? "sign-in" : "sign-up"}/`}
|
action={`${API_BASE_URL}/auth/spaces/${mode === EAuthModes.SIGN_IN ? "sign-in" : "sign-up"}/`}
|
||||||
onSubmit={() => setIsSubmitting(true)}
|
onSubmit={async (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
await handleCSRFToken();
|
||||||
|
formRef.current && formRef.current.submit();
|
||||||
|
setIsSubmitting(true);
|
||||||
|
}}
|
||||||
onError={() => setIsSubmitting(false)}
|
onError={() => setIsSubmitting(false)}
|
||||||
>
|
>
|
||||||
<input type="hidden" name="csrfmiddlewaretoken" value={csrfToken} />
|
<input type="hidden" name="csrfmiddlewaretoken" />
|
||||||
<input type="hidden" value={passwordFormData.email} name="email" />
|
<input type="hidden" value={passwordFormData.email} name="email" />
|
||||||
<input type="hidden" value={nextPath} name="next_path" />
|
<input type="hidden" value={nextPath} name="next_path" />
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import React, { useEffect, useMemo, useState } from "react";
|
import React, { useEffect, useMemo, useRef, useState } from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
// icons
|
// icons
|
||||||
|
|
@ -51,8 +51,10 @@ export const AuthPasswordForm: React.FC<Props> = observer((props: Props) => {
|
||||||
const { email, isSMTPConfigured, handleAuthStep, handleEmailClear, mode, nextPath } = props;
|
const { email, isSMTPConfigured, handleAuthStep, handleEmailClear, mode, nextPath } = props;
|
||||||
// hooks
|
// hooks
|
||||||
const { captureEvent } = useEventTracker();
|
const { captureEvent } = useEventTracker();
|
||||||
|
// ref
|
||||||
|
const formRef = useRef<HTMLFormElement>(null);
|
||||||
// states
|
// states
|
||||||
const [csrfToken, setCsrfToken] = useState<string | undefined>(undefined);
|
const [csrfPromise, setCsrfPromise] = useState<Promise<{ csrf_token: string }> | undefined>(undefined);
|
||||||
const [passwordFormData, setPasswordFormData] = useState<TPasswordFormValues>({ ...defaultValues, email });
|
const [passwordFormData, setPasswordFormData] = useState<TPasswordFormValues>({ ...defaultValues, email });
|
||||||
const [showPassword, setShowPassword] = useState({
|
const [showPassword, setShowPassword] = useState({
|
||||||
password: false,
|
password: false,
|
||||||
|
|
@ -70,9 +72,11 @@ export const AuthPasswordForm: React.FC<Props> = observer((props: Props) => {
|
||||||
setPasswordFormData((prev) => ({ ...prev, [key]: value }));
|
setPasswordFormData((prev) => ({ ...prev, [key]: value }));
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (csrfToken === undefined)
|
if (csrfPromise === undefined) {
|
||||||
authService.requestCSRFToken().then((data) => data?.csrf_token && setCsrfToken(data.csrf_token));
|
const promise = authService.requestCSRFToken();
|
||||||
}, [csrfToken]);
|
setCsrfPromise(promise);
|
||||||
|
}
|
||||||
|
}, [csrfPromise]);
|
||||||
|
|
||||||
const redirectToUniqueCodeSignIn = async () => {
|
const redirectToUniqueCodeSignIn = async () => {
|
||||||
handleAuthStep(EAuthSteps.UNIQUE_CODE);
|
handleAuthStep(EAuthSteps.UNIQUE_CODE);
|
||||||
|
|
@ -115,6 +119,14 @@ export const AuthPasswordForm: React.FC<Props> = observer((props: Props) => {
|
||||||
const confirmPassword = passwordFormData?.confirm_password ?? "";
|
const confirmPassword = passwordFormData?.confirm_password ?? "";
|
||||||
const renderPasswordMatchError = !isRetryPasswordInputFocused || confirmPassword.length >= password.length;
|
const renderPasswordMatchError = !isRetryPasswordInputFocused || confirmPassword.length >= password.length;
|
||||||
|
|
||||||
|
const handleCSRFToken = async () => {
|
||||||
|
if (!formRef || !formRef.current) return;
|
||||||
|
const token = await csrfPromise;
|
||||||
|
if (!token?.csrf_token) return;
|
||||||
|
const csrfElement = formRef.current.querySelector("input[name=csrfmiddlewaretoken]");
|
||||||
|
csrfElement?.setAttribute("value", token?.csrf_token);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{isBannerMessage && mode === EAuthModes.SIGN_UP && (
|
{isBannerMessage && mode === EAuthModes.SIGN_UP && (
|
||||||
|
|
@ -132,11 +144,13 @@ export const AuthPasswordForm: React.FC<Props> = observer((props: Props) => {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<form
|
<form
|
||||||
|
ref={formRef}
|
||||||
className="mt-5 space-y-4"
|
className="mt-5 space-y-4"
|
||||||
method="POST"
|
method="POST"
|
||||||
action={`${API_BASE_URL}/auth/${mode === EAuthModes.SIGN_IN ? "sign-in" : "sign-up"}/`}
|
action={`${API_BASE_URL}/auth/${mode === EAuthModes.SIGN_IN ? "sign-in" : "sign-up"}/`}
|
||||||
onSubmit={(event) => {
|
onSubmit={async (event) => {
|
||||||
event.preventDefault(); // Prevent form from submitting by default
|
event.preventDefault(); // Prevent form from submitting by default
|
||||||
|
await handleCSRFToken();
|
||||||
const isPasswordValid =
|
const isPasswordValid =
|
||||||
mode === EAuthModes.SIGN_UP
|
mode === EAuthModes.SIGN_UP
|
||||||
? getPasswordStrength(passwordFormData.password) === E_PASSWORD_STRENGTH.STRENGTH_VALID
|
? getPasswordStrength(passwordFormData.password) === E_PASSWORD_STRENGTH.STRENGTH_VALID
|
||||||
|
|
@ -144,14 +158,14 @@ export const AuthPasswordForm: React.FC<Props> = observer((props: Props) => {
|
||||||
if (isPasswordValid) {
|
if (isPasswordValid) {
|
||||||
setIsSubmitting(true);
|
setIsSubmitting(true);
|
||||||
captureEvent(mode === EAuthModes.SIGN_IN ? SIGN_IN_WITH_PASSWORD : SIGN_UP_WITH_PASSWORD);
|
captureEvent(mode === EAuthModes.SIGN_IN ? SIGN_IN_WITH_PASSWORD : SIGN_UP_WITH_PASSWORD);
|
||||||
event.currentTarget.submit(); // Manually submit the form if the condition is met
|
formRef.current && formRef.current.submit(); // Manually submit the form if the condition is met
|
||||||
} else {
|
} else {
|
||||||
setBannerMessage(true);
|
setBannerMessage(true);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onError={() => setIsSubmitting(false)}
|
onError={() => setIsSubmitting(false)}
|
||||||
>
|
>
|
||||||
<input type="hidden" name="csrfmiddlewaretoken" value={csrfToken} />
|
<input type="hidden" name="csrfmiddlewaretoken" />
|
||||||
<input type="hidden" value={passwordFormData.email} name="email" />
|
<input type="hidden" value={passwordFormData.email} name="email" />
|
||||||
{nextPath && <input type="hidden" value={nextPath} name="next_path" />}
|
{nextPath && <input type="hidden" value={nextPath} name="next_path" />}
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue