fix: trim whitespace from username on login and registration (#277)

Add .trim() to both loginSchema and registerSchema Zod validators so
leading/trailing spaces are stripped before validation and DB lookup.
Includes 5 new test cases covering trim behavior for both endpoints.
This commit is contained in:
Daniel Volz
2026-02-22 17:51:41 +01:00
committed by GitHub
parent c620146c4b
commit d0e2ee0783
2 changed files with 82 additions and 1 deletions
+2 -1
View File
@@ -53,6 +53,7 @@ const sensitiveRateLimitConfig = {
const registerSchema = z.object({
username: z
.string()
.trim()
.min(3, "Username must be at least 3 characters")
.max(50, "Username must be at most 50 characters")
.regex(/^[a-zA-Z0-9_-]+$/, "Username can only contain letters, numbers, underscores, and hyphens"),
@@ -63,7 +64,7 @@ const registerSchema = z.object({
});
const loginSchema = z.object({
username: z.string().min(1, "Username is required"),
username: z.string().trim().min(1, "Username is required"),
password: z.string().min(1, "Password is required"),
rememberMe: z.boolean().optional().default(false),
});
+80
View File
@@ -245,6 +245,57 @@ describe("Auth Routes (AUTH_ENABLED=true)", () => {
expect(response.json().code).toBe("VALIDATION_ERROR");
});
it("should register with trimmed username when input has whitespace", async () => {
const response = await app.inject({
method: "POST",
url: "/auth/register",
payload: {
username: " trimuser ",
password: "TestPassword123",
},
});
expect(response.statusCode).toBe(201);
expect(response.json().user.username).toBe("trimuser");
});
it("should reject whitespace-only username on registration", async () => {
const response = await app.inject({
method: "POST",
url: "/auth/register",
payload: {
username: " ",
password: "TestPassword123",
},
});
expect(response.statusCode).toBe(400);
expect(response.json().code).toBe("VALIDATION_ERROR");
});
it("should reject duplicate username even with surrounding whitespace", async () => {
await app.inject({
method: "POST",
url: "/auth/register",
payload: {
username: "spacedupe",
password: "TestPassword123",
},
});
const response = await app.inject({
method: "POST",
url: "/auth/register",
payload: {
username: " spacedupe ",
password: "AnotherPassword123",
},
});
expect(response.statusCode).toBe(409);
expect(response.json().code).toBe("USERNAME_EXISTS");
});
it("should reject invalid username characters", async () => {
const response = await app.inject({
method: "POST",
@@ -341,6 +392,35 @@ describe("Auth Routes (AUTH_ENABLED=true)", () => {
expect(response.json().code).toBe("INVALID_CREDENTIALS");
});
it("should login successfully when username has leading/trailing whitespace", async () => {
const response = await app.inject({
method: "POST",
url: "/auth/login",
payload: {
username: " loginuser ",
password: "TestPassword123",
},
});
expect(response.statusCode).toBe(200);
expect(response.json().ok).toBe(true);
expect(response.json().user.username).toBe("loginuser");
});
it("should reject whitespace-only username on login", async () => {
const response = await app.inject({
method: "POST",
url: "/auth/login",
payload: {
username: " ",
password: "TestPassword123",
},
});
expect(response.statusCode).toBe(400);
expect(response.json().code).toBe("VALIDATION_ERROR");
});
it("should support rememberMe option", async () => {
const response = await app.inject({
method: "POST",