fix(auth): validate email field (#8698)

This commit is contained in:
Alejandro Bailo
2025-09-11 15:29:49 +02:00
committed by GitHub
parent 82cd29d595
commit 6f967c6da7
6 changed files with 29 additions and 25 deletions

View File

@@ -11,6 +11,7 @@ All notable changes to the **Prowler UI** are documented in this file.
### 🐞 Fixed
- Field-level email validation message [(#8698)] (https://github.com/prowler-cloud/prowler/pull/8698)
- POST method on auth form [(#8699)] (https://github.com/prowler-cloud/prowler/pull/8699)
## [1.12.0] (Prowler v5.12.0)

View File

@@ -35,7 +35,7 @@ export async function authenticate(
message: "Credentials error",
errors: {
...defaultValues,
credentials: "Incorrect email or password",
credentials: "Invalid email or password",
},
};
case "CallbackRouteError":

View File

@@ -12,7 +12,7 @@ import { authenticate, createNewUser } from "@/actions/auth";
import { initiateSamlAuth } from "@/actions/integrations/saml";
import { PasswordRequirementsMessage } from "@/components/auth/oss/password-validator";
import { SocialButtons } from "@/components/auth/oss/social-buttons";
import { NotificationIcon, ProwlerExtended } from "@/components/icons";
import { ProwlerExtended } from "@/components/icons";
import { ThemeSwitch } from "@/components/ThemeSwitch";
import { useToast } from "@/components/ui";
import { CustomButton, CustomInput } from "@/components/ui/custom";
@@ -65,6 +65,8 @@ export const AuthForm = ({
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
mode: "onSubmit",
reValidateMode: "onSubmit",
defaultValues: {
email: "",
password: "",
@@ -111,10 +113,11 @@ export const AuthForm = ({
if (result?.message === "Success") {
router.push("/");
} else if (result?.errors && "credentials" in result.errors) {
form.setError("email", {
type: "server",
message: result.errors.credentials ?? "Incorrect email or password",
});
const message =
result.errors.credentials ?? "Invalid email or password";
form.setError("email", { type: "server", message });
form.setError("password", { type: "server", message });
} else if (result?.message === "User email is not verified") {
router.push("/email-verification");
} else {
@@ -206,6 +209,7 @@ export const AuthForm = ({
<Form {...form}>
<form
noValidate
method="post"
className="flex flex-col gap-4"
onSubmit={form.handleSubmit(onSubmit)}
@@ -238,7 +242,8 @@ export const AuthForm = ({
label="Email"
placeholder="Enter your email"
isInvalid={!!form.formState.errors.email}
showFormMessage={type !== "sign-in"}
// Always show field validation message, including on sign-in
showFormMessage
/>
{!isSamlMode && (
<>
@@ -246,10 +251,8 @@ export const AuthForm = ({
control={form.control}
name="password"
password
isInvalid={
!!form.formState.errors.password ||
!!form.formState.errors.email
}
// Only mark invalid when the password field has an error
isInvalid={!!form.formState.errors.password}
/>
{type === "sign-up" && (
<PasswordRequirementsMessage
@@ -320,12 +323,7 @@ export const AuthForm = ({
)}
</>
)}
{type === "sign-in" && form.formState.errors?.email && (
<div className="flex flex-row items-center text-system-error">
<NotificationIcon size={16} />
<p className="text-small">Invalid email or password</p>
</div>
)}
<CustomButton
type="submit"
ariaLabel={type === "sign-in" ? "Log in" : "Sign up"}

View File

@@ -48,22 +48,20 @@ test.describe("Login Flow", () => {
test("should handle empty form submission", async ({ page }) => {
// Submit empty form
await submitLoginForm(page);
await verifyLoginError(page, ERROR_MESSAGES.INVALID_CREDENTIALS);
await verifyLoginError(page, ERROR_MESSAGES.INVALID_EMAIL);
// Verify we're still on login page
await expect(page).toHaveURL(URLS.LOGIN);
});
/*
TODO: This test is failing, need UI work before.
test("should validate email format", async ({ page }) => {
// Attempt login with invalid email format
await login(page, TEST_CREDENTIALS.INVALID_EMAIL_FORMAT);
// Verify error message (application shows generic error for invalid email format too)
await verifyLoginError(page, ERROR_MESSAGES.INVALID_CREDENTIALS);
// Verify field-level email validation message
await verifyLoginError(page, ERROR_MESSAGES.INVALID_EMAIL);
// Verify we're still on login page
await expect(page).toHaveURL(URLS.LOGIN);
});
*/
test("should toggle SAML SSO mode", async ({ page }) => {
// Toggle to SAML mode

View File

@@ -2,6 +2,7 @@ import { Page, expect } from "@playwright/test";
export const ERROR_MESSAGES = {
INVALID_CREDENTIALS: "Invalid email or password",
INVALID_EMAIL: "Please enter a valid email address.",
} as const;
export const URLS = {
@@ -69,7 +70,8 @@ export async function verifyLoginError(
page: Page,
errorMessage = "Invalid email or password",
) {
await expect(page.getByText(errorMessage)).toBeVisible();
// There may be multiple field-level errors with the same text; assert at least one is visible
await expect(page.getByText(errorMessage).first()).toBeVisible();
await expect(page).toHaveURL("/sign-in");
}

View File

@@ -96,7 +96,12 @@ export const authFormSchema = (type: string) =>
}),
// Fields for Sign In and Sign Up
email: z.string().email(),
// Trim and normalize email, and provide consistent message
email: z
.string()
.trim()
.toLowerCase()
.email({ message: "Please enter a valid email address." }),
password: type === "sign-in" ? z.string() : validatePassword(),
isSamlMode: z.boolean().optional(),
})