Skip to content

fix(ux/login): field-specific error copy for empty + malformed inputs - #485

Merged
cristim merged 2 commits into
feat/multicloud-web-frontendfrom
fix/issues-455-456-login-error-copy
May 19, 2026
Merged

fix(ux/login): field-specific error copy for empty + malformed inputs#485
cristim merged 2 commits into
feat/multicloud-web-frontendfrom
fix/issues-455-456-login-error-copy

Conversation

@cristim

@cristim cristim commented May 19, 2026

Copy link
Copy Markdown
Member

Summary

Replace the generic / wrong-field error copy on the login modal with field-specific, user-actionable messages, while preserving the account-enumeration mitigation already in the backend.

  • validateLoginInputs runs as a client-side pre-flight before the rate-limit check, so an accidental click on a blank form does not burn the cooldown the user needs for their real attempt. Empty / both-empty / malformed inputs are caught locally and produce "Enter email address", "Enter password", "Enter email and password", or "Incorrect email format".
  • mapServerLoginError translates the known generic backend strings into clearer copy. The backend's authentication failed (user-not-found) and invalid email or password (wrong-password) BOTH collapse to a single client-side message ("Incorrect email or password"), so the enumeration mitigation called out in ux(login): wrong-data error messages are generic ("authentication failed") and hide which field is wrong #456 is preserved. Unknown server messages pass through unchanged so MFA / rate-limit / server signals are not suppressed.
  • No backend changes. The backend remains the authoritative validator for email format and credentials.

Test plan

  • 8 new component tests in frontend/src/__tests__/auth.test.ts cover empty email, empty password, both-empty, malformed email, backend authentication failed, backend invalid email or password, backend invalid email format, and the unknown-error pass-through.
  • Full frontend test suite green (1750 passed, 1 skipped, 0 failed).
  • tsc --noEmit clean.
  • Manual smoke against a deployed frontend: submit each branch (empty, empty/empty, malformed, wrong creds) and confirm the message text + the modal stays open.

Security note

The QA suggestion in #456 for cases 3.2 / 3.5 (distinguish "user does not exist" from "wrong password") is intentionally NOT applied. Doing so would leak account-existence information and weaken the brute-force / enumeration mitigation that the backend already enforces by returning the generic invalid email or password from both paths. The user-visible improvement here comes from catching empty / malformed inputs locally before the request goes out and from rewording the generic credential message; the security-critical merge of "wrong-user" and "wrong-password" stays.

Closes #455, #456.

Summary by CodeRabbit

  • New Features

    • Client-side login validation for empty email/password and email format; clearer, normalized user-facing error messages.
    • Login flow now safely handles unexpected server error shapes so errors display without breaking the UI.
  • Tests

    • Expanded login tests covering pre-flight validation, normalized server errors, and defensive handling of non-standard error responses.

Review Change Stack

Surface actionable, field-targeted messages on the login modal instead of
the generic backend strings users were seeing on empty / malformed input.

Issue #455: empty email previously showed "Invalid email format" and
empty password showed "Invalid email or password", neither of which
told the user which field was actually blank.

Issue #456 (case 3.3): a malformed email previously came back from the
server as "authentication failed", which read like a credential
problem rather than a typo.

Changes (frontend/src/auth.ts):
  * `validateLoginInputs` pre-flight runs before the rate-limit check so
    an accidental click on a blank form does not burn the cooldown.
    Maps empty / both-empty / malformed to "Enter email address",
    "Enter password", "Enter email and password", "Incorrect email
    format".
  * `mapServerLoginError` translates the known generic backend strings
    to clearer copy. "authentication failed" and "invalid email or
    password" both collapse to "Incorrect email or password" so the
    account-enumeration mitigation called out in #456 is preserved
    (the backend stays generic on the credential check). Unknown
    server messages pass through unchanged so MFA / rate-limit /
    server signals are not suppressed.
  * Extracted `showLoginError` to remove three near-duplicate error-div
    update blocks.

Tests: 8 new component tests in frontend/src/__tests__/auth.test.ts
cover each branch; full suite (1750 tests) green.

Closes #455, #456.
@cristim cristim added bug Something isn't working triaged Item has been triaged priority/p2 Backlog-worthy severity/medium Moderate harm urgency/this-sprint Within the current sprint impact/many Affects most users effort/s Hours type/bug Defect labels May 19, 2026
@coderabbitai

coderabbitai Bot commented May 19, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fa70e51c-6671-435e-bb96-06d77bc6568f

📥 Commits

Reviewing files that changed from the base of the PR and between 1cd74b9 and a8a5b94.

📒 Files selected for processing (2)
  • frontend/src/__tests__/auth.test.ts
  • frontend/src/auth.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • frontend/src/tests/auth.test.ts
  • frontend/src/auth.ts

📝 Walkthrough

Walkthrough

Client-side pre-flight checks and a server-error mapper were added to the login flow; the handler integrates these and defensively stringifies non-Error rejections. Tests cover empty/malformed inputs, mapped server errors, pass-throughs, and non-Error rejections.

Changes

Login form validation and error handling

Layer / File(s) Summary
Email and password validation helper
frontend/src/auth.ts
EMAIL_SHAPE_RE regex and validateLoginInputs function produce targeted error messages for missing email, missing password, both empty, or obviously malformed email addresses.
Backend error message mapping
frontend/src/auth.ts
mapServerLoginError translates known backend login failure strings ("authentication failed", "invalid email or password", "invalid email format") into clearer client-facing messages; unknown backend errors pass through unchanged.
Login error renderer
frontend/src/auth.ts
showLoginError centralizes rendering the login error into #login-error and ensures it is visible.
Login handler refactor & defensive handling
frontend/src/auth.ts
handleLogin now trims inputs, runs pre-flight validation before rate-limit cooldown, hides prior errors before API call, displays mapped server messages on failure, and stringifies non-Error rejections rather than assuming Error objects.
Test coverage for validation and error handling
frontend/src/__tests__/auth.test.ts
submitLogin helper and new tests verify client-side pre-flight validation, backend-to-display error mappings, pass-through behavior for unmapped backend errors, and defensive handling of string/plain-object rejections.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

Suggested labels

priority/p3, severity/low, urgency/this-quarter, impact/all-users

Poem

🐰 I nibble fields so forms behave,
Empty hops I catch and save.
Server grumbles get friendlier sound,
Errors shown, not thrown around.
Tests clap paws — the login's brave!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding field-specific error messages for empty and malformed login inputs, which is the core of the PR.
Linked Issues check ✅ Passed The PR fully addresses issue #455 by implementing client-side pre-flight validation with field-specific messages (empty email, empty password, both empty, malformed email) and server error mapping as required.
Out of Scope Changes check ✅ Passed All changes are in scope: client-side validation logic and tests directly support the linked issue requirements; no unrelated modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/issues-455-456-login-error-copy

Comment @coderabbitai help to get the list of available commands and usage tips.

@cristim

cristim commented May 19, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 19, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@frontend/src/auth.ts`:
- Around line 524-526: The code casts `err` to `Error` unsafely in
mapServerLoginError; change `mapServerLoginError` to accept an `unknown` error,
then defensively extract a message: if `err instanceof Error` use `err.message`,
else use `String(err)` (or a default like `''`) before calling `.toLowerCase()`
so `mapServerLoginError` and any callers (e.g., where `api.login()` rejections
are handled) won't throw when a non-Error rejection arrives.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: bf101751-b558-4283-9589-1866b338b98f

📥 Commits

Reviewing files that changed from the base of the PR and between 90f37e4 and 1cd74b9.

📒 Files selected for processing (2)
  • frontend/src/__tests__/auth.test.ts
  • frontend/src/auth.ts

Comment thread frontend/src/auth.ts
CodeRabbit flagged that `error as Error` in the catch block can produce
`undefined.toLowerCase()` inside `mapServerLoginError` if `api.login`
rejects with a non-Error value (string, plain object, etc.). Replace
the unsafe cast with an `instanceof Error` guard that falls back to
`String(error)`.

Adds two regression tests covering string and plain-object rejections.
@cristim

cristim commented May 19, 2026

Copy link
Copy Markdown
Member Author

Addressed CodeRabbit pass 1: replaced the error as Error cast in handleLogin with an instanceof Error guard + String(error) fallback so non-Error rejections (string, plain object) no longer cause undefined.toLowerCase() inside mapServerLoginError. Added two regression tests covering string + plain-object rejections (39 tests pass, typecheck clean).

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 19, 2026

Copy link
Copy Markdown
Contributor

@cristim Great work on the follow-up — the instanceof Error guard with String(error) fallback is the correct pattern for safely narrowing unknown catch bindings, and the regression tests for string and plain-object rejections are exactly the right safety net to have here. Triggering a fresh review now.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cristim

cristim commented May 19, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 19, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working effort/s Hours impact/many Affects most users priority/p2 Backlog-worthy severity/medium Moderate harm triaged Item has been triaged type/bug Defect urgency/this-sprint Within the current sprint

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant