Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion web_src/src/pages/auth/Login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export const Login: React.FC = () => {
const [signupEmail, setSignupEmail] = useState("");
const [signupPassword, setSignupPassword] = useState("");
const [signupConfirmPassword, setSignupConfirmPassword] = useState("");
const [signupFieldErrors, setSignupFieldErrors] = useState<Record<string, string>>({});
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Field errors not cleared when toggling modes

Low Severity

The newly introduced signupFieldErrors state is not cleared in handleToggleMode (which clears formError but not signupFieldErrors). If a user triggers password field errors, switches to login mode, and then switches back to signup mode, the stale red error text remains visible on the password field instead of reverting to the hint.

Fix in Cursor Fix in Web


const [magicCodeStep, setMagicCodeStep] = useState<MagicCodeStep>("email");
const [magicCodeEmail, setMagicCodeEmail] = useState("");
Expand Down Expand Up @@ -359,6 +360,13 @@ export const Login: React.FC = () => {
}
};

const isPasswordValid = (password: string) => {
if (password.length < 8) return false;
if (!/[0-9]/.test(password)) return false;
if (!/[A-Z]/.test(password)) return false;
return true;
};
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicated isPasswordValid function across two files

Low Severity

isPasswordValid is duplicated verbatim in both Login.tsx and OwnerSetup.tsx. If password requirements change, both copies need to be updated in lockstep, which is error-prone. This is a good candidate for extraction into a shared utility.

Fix in Cursor Fix in Web


const handleSignupSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setFormError(null);
Expand All @@ -368,6 +376,8 @@ export const Login: React.FC = () => {
return;
}

const fieldErrors: Record<string, string> = {};

if (!signupFirstName.trim() || !signupLastName.trim()) {
setFormError("First and last names are required");
return;
Expand All @@ -378,8 +388,16 @@ export const Login: React.FC = () => {
return;
}

if (!isPasswordValid(signupPassword)) {
fieldErrors.password = "Password must be 8+ characters with at least 1 number and 1 capital letter.";
}

if (signupPassword !== signupConfirmPassword) {
setFormError("Passwords do not match");
fieldErrors.confirmPassword = "Passwords do not match.";
}

setSignupFieldErrors(fieldErrors);
if (Object.keys(fieldErrors).length > 0) {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale field errors persist after early return

Medium Severity

handleSignupSubmit clears formError at the top (line 372) but never clears signupFieldErrors. The call to setSignupFieldErrors(fieldErrors) only happens at line 399, which is after the early returns for missing name (line 383) and missing email/password (line 388). If a user first triggers a password field error, then triggers one of those early returns on the next submit, the stale red password error text remains visible alongside the new formError banner.

Fix in Cursor Fix in Web

return;
}

Expand Down Expand Up @@ -609,6 +627,11 @@ export const Login: React.FC = () => {
value={signupPassword}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setSignupPassword(e.target.value)}
/>
{signupFieldErrors.password ? (
<p className="text-xs text-red-600">{signupFieldErrors.password}</p>
) : (
<p className="text-xs text-gray-500">8+ characters, at least 1 number and 1 capital letter</p>
)}
</div>

<div className="space-y-2">
Expand All @@ -622,6 +645,9 @@ export const Login: React.FC = () => {
value={signupConfirmPassword}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setSignupConfirmPassword(e.target.value)}
/>
{signupFieldErrors.confirmPassword && (
<p className="text-xs text-red-600">{signupFieldErrors.confirmPassword}</p>
)}
</div>

<LoadingButton
Expand Down
Loading