Lost Password Modernized - sebiboga/testlink-upgraded GitHub Wiki

Lost Password / Password Reset โ€” Modernized Screen

Modernization of the legacy lost-password screen (lostPassword.php) โ€” GitHub issue #783.

The legacy Smarty screen is replaced by a standalone Dashio page (gui/templates/auth/lostPassword.html) backed by a plain-PHP REST BFF action in api/auth/index.php (POST /api/auth/reset). The login page "Lost password?" link now points to the modern HTML screen.

Path: login page โ†’ Lost password? URL: gui/templates/auth/lostPassword.html BFF API: POST /api/auth/reset Rights: public (pre-authentication entry screen). Tracking issue: #783


Screen layout

The modern lost-password screen is a single-page Dashio-styled form:

Section Description
Card Centered white card over a background image
Heading TestLink logo + "Lost Password"
Hint "Enter your user ID and we will email you a new password."
Field User ID (required)
Action "Send" button (loading state, disabled during submit)
Footer "Back to login" link + version + GitHub repo link

Reset flow

  1. The user enters their login and clicks Send.
  2. POST /api/auth/reset is called with the login.
  3. The BFF returns a generic success body for every path (see Security below), the page shows the generic "If this user exists, a new password was sent..." note, then redirects to login.html?note=lost.
  4. For a real user whose auth method manages passwords internally, a new password is generated and emailed to the registered address (SMTP via MailDev locally) and a PWD_RESET audit event is written.

Security: enumeration-safe reset

The reset endpoint must not let an anonymous caller discover whether a login exists, its authentication method, or its email state. The BFF therefore returns the identical {"status":"ok","success":true} body for:

  • an unknown login
  • an existing user on external password management (reset skipped, audited)
  • an existing user without a registered email
  • a successful reset

Internal distinctions are only logged server-side. This fixes the original user-enumeration oracles (bad_user, password_mgmt_is_external, mail_empty_address) โ€” bug #784.

Validation & error states

Case Result
Empty login auth.requiredResetUser (client-side)
Mail transport misconfigured handled without a 500; generic body (no user leak)
External password mgmt reset skipped + audit, still generic success

API endpoints

Method Endpoint Description
POST /api/auth/reset request a password reset (generic, enumeration-safe)

POST routes are protected by the shared same-origin CSRF guard (_guard.php).

i18n

All labels, placeholders, links and messages use client-side TLi18n keys under the auth.* namespace, present in every locale bundle (gui/templates/i18n/{en,ro,de,es,fr,it,ja,pt,ru,zh}.json).

Regression suite

Executed as Suite 783 โ€” 10/10 PASS (2026-08-31, headless Chrome + MariaDB testlink @127.0.0.1). Coverage: default render + i18n, empty-submit validation, unknown-login enumeration-safe generic success, existing local user with mail unconfigured (no 500 โ€” resetPassword() persists the new password only after email_send succeeds, so nothing is written and no user's password is silently invalidated), redirect to login.html?note=lost with the "Password recovery completed." note, login-page link wiring (firstLogin.html / lostPassword.html), BFF GET /api/auth/config, CSRF guard on POST /api/auth/reset (403 for non-same-origin requests), i18n completeness in all 10 bundles, and Event Viewer cleanliness (zero new Error/Warning events rows).

Screenshots are on the GitHub Wiki page (Lost-Password-Modernized.md).

Re-verification 2026-09-21 (task #1343 โ€” "Nothing missing" confirmed)

Full parity re-check on a freshly imported DB (PHP built-in server http://localhost:8082, users admin / resetuser DB+email / reselext LDAP). Result: 7/7 PASS, zero gaps โ€” the entry path fix (#1338, absolute href, merged b895957f5) works from the standard /login.php entry; empty submit is blocked natively (no POST); unknown + existing + external + no-email users all receive the byte-identical {"status":"ok","success":true} (enumeration-safe); no password hash changes when SMTP is unconfigured; CSRF guard returns 403 without Origin; the 0..30 login cap is mirrored server-side; i18n keys live in all 10 bundles; the events table stays clean (0 Error/Warning rows). Evidence: Suite 1343 in tmp/TLU_Test_Cases.md + docs/screenshots/issue-1343-lostpw-default.png and issue-1343-lostpw-unknown-success-redirect.png. Cleanup task #1344 is unblocked.


TestLink 2.0.1 ยท Lost Password screen ยท Refs #783 ยท parity re-verified Refs #1343