Skip to content

Commit 5e2ad57

Browse files
authored
Merge pull request #226 from devondragon/feature/password-validation-fixes
Feature/password validation fixes
2 parents a6a3998 + e3fbb96 commit 5e2ad57

File tree

10 files changed

+1059
-2
lines changed

10 files changed

+1059
-2
lines changed

DEMO_APP_CHANGES_REQUIRED.md

Lines changed: 297 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,297 @@
1+
# Demo App Changes Required for Password Validation Fixes
2+
3+
This document outlines the changes needed in the SpringUserFrameworkDemoApp to work with the password validation security fixes.
4+
5+
---
6+
7+
## Critical Changes (Required for Password Reset to Work)
8+
9+
### 1. Fix Password Reset Form Action
10+
**Status:** ❌ BROKEN - Password reset doesn't work
11+
**Priority:** CRITICAL
12+
**Impact:** Password reset flow is currently non-functional
13+
14+
#### Issue
15+
The forgot-password-change form posts to the wrong endpoint:
16+
- **Current:** Form posts to `/user/resetPassword` (which only sends email)
17+
- **Expected:** Form posts to `/user/savePassword` (new endpoint that saves password)
18+
19+
#### Files to Change
20+
21+
**File:** `SpringUserFrameworkDemoApp/src/main/resources/templates/user/forgot-password-change.html`
22+
23+
**Line 25:** Change form action
24+
```html
25+
<!-- BEFORE -->
26+
<form id="resetPasswordForm" th:action="@{/user/resetPassword}" method="POST">
27+
28+
<!-- AFTER -->
29+
<form id="resetPasswordForm" th:action="@{/user/savePassword}" method="POST">
30+
```
31+
32+
#### Field Name Validation
33+
The current field names are CORRECT and match the new SavePasswordDto:
34+
-`name="newPassword"` matches `SavePasswordDto.newPassword`
35+
-`name="token"` matches `SavePasswordDto.token`
36+
-`id="matchPassword"` is client-side only (not sent to server, which is correct)
37+
38+
The JavaScript sends `confirmPassword` in the payload, which needs to be added to the form:
39+
40+
**Line 33:** Add name attribute to confirmPassword field
41+
```html
42+
<!-- BEFORE -->
43+
<input type="password" id="matchPassword" class="form-control" required>
44+
45+
<!-- AFTER -->
46+
<input type="password" id="matchPassword" name="confirmPassword" class="form-control" required>
47+
```
48+
49+
---
50+
51+
## Optional Improvements (Non-Breaking, Enhances UX)
52+
53+
### 2. Update Password Field Name Mismatch
54+
**Status:** ⚠️ WORKING but inconsistent
55+
**Priority:** MEDIUM
56+
**Impact:** Update password works, but field names don't match backend DTO
57+
58+
#### Issue
59+
The update-password form uses `currentPassword` but the backend expects `oldPassword`:
60+
61+
**JavaScript sends:**
62+
```javascript
63+
{
64+
currentPassword: "...", // ❌ This field name
65+
newPassword: "...",
66+
confirmPassword: "..."
67+
}
68+
```
69+
70+
**Backend PasswordDto expects:**
71+
```java
72+
private String oldPassword; // ✅ This field name
73+
private String newPassword;
74+
```
75+
76+
**However:** This currently WORKS because Spring's model binding is lenient and maps it correctly, but it's inconsistent.
77+
78+
#### Recommended Fix (Optional)
79+
80+
**File:** `SpringUserFrameworkDemoApp/src/main/resources/static/js/user/update-password.js`
81+
82+
**Lines 27-31:** Update field name
83+
```javascript
84+
// BEFORE
85+
const requestData = {
86+
currentPassword: currentPassword,
87+
newPassword: newPassword,
88+
confirmPassword: confirmPassword,
89+
};
90+
91+
// AFTER
92+
const requestData = {
93+
oldPassword: currentPassword, // Changed to match backend DTO
94+
newPassword: newPassword,
95+
};
96+
// Note: confirmPassword removed - not needed by backend
97+
```
98+
99+
**Alternative:** Keep it as-is since it's working. The inconsistency is minor.
100+
101+
---
102+
103+
## Enhanced User Experience Changes
104+
105+
### 3. Add Password Strength Meter to Other Forms
106+
**Status:** ℹ️ Enhancement (Partial - only on registration)
107+
**Priority:** LOW
108+
**Impact:** Users get better feedback on password requirements
109+
110+
#### Current State
111+
The Demo App already has a password strength meter on the **registration form** (`register.js`):
112+
- ✅ Real-time strength calculation (Very Weak → Very Strong)
113+
- ✅ Visual progress bar with color coding
114+
- ✅ Password requirements checklist
115+
- ✅ Checks for length, uppercase, lowercase, digits, special chars
116+
117+
**Files with existing implementation:**
118+
- `src/main/resources/static/js/user/register.js` (lines 49-188)
119+
- `src/main/resources/templates/user/register.html`
120+
121+
#### Enhancement Opportunity
122+
Reuse the existing password strength meter code on:
123+
1. **Password reset form** (`forgot-password-change.html`)
124+
2. **Update password form** (`update-password.html`)
125+
126+
**Implementation Steps:**
127+
1. Extract `calculateStrength()` and `updateStrengthBar()` functions to a shared module:
128+
- Create: `static/js/utils/password-validation.js`
129+
- Export functions for reuse
130+
131+
2. Add strength meter UI to other password forms:
132+
- Modify: `templates/user/forgot-password-change.html`
133+
- Modify: `templates/user/update-password.html`
134+
- Import and use the shared password validation module
135+
136+
3. Add password requirements checklist to both forms:
137+
```html
138+
<div id="password-requirements" class="small text-muted mt-2 d-none">
139+
<div>Password must contain:</div>
140+
<div>• At least 8 characters</div>
141+
<div>• An uppercase letter (A-Z)</div>
142+
<div>• A lowercase letter (a-z)</div>
143+
<div>• A number (0-9)</div>
144+
<div>• A special character</div>
145+
</div>
146+
```
147+
148+
**Estimated Effort:** 1-2 hours (much less than creating from scratch)
149+
150+
**Note:** This is a UX enhancement and not required for functionality. The backend validation already enforces all rules.
151+
152+
---
153+
154+
## Testing Checklist
155+
156+
After making the required changes, test these scenarios:
157+
158+
### Password Reset Flow (Critical)
159+
- [ ] Request password reset email
160+
- [ ] Click link in email (opens forgot-password-change.html)
161+
- [ ] Enter weak password (too short)
162+
- Expected: Error message about password requirements
163+
- [ ] Enter password that matches old password
164+
- Expected: Error message about password history
165+
- [ ] Enter valid strong password
166+
- Expected: Success message, redirected to login
167+
- [ ] Log in with new password
168+
- Expected: Login succeeds
169+
- [ ] Try to reuse the reset token
170+
- Expected: Error - token already used/invalid
171+
172+
### Update Password Flow (Should Continue Working)
173+
- [ ] Log in as existing user
174+
- [ ] Navigate to update-password page
175+
- [ ] Enter wrong old password
176+
- Expected: Error "The old password is incorrect" (error code 1)
177+
- [ ] Enter correct old password but weak new password
178+
- Expected: Error about password requirements (error code 2)
179+
- [ ] Enter correct old password and password from history
180+
- Expected: Error about password reuse (error code 2)
181+
- [ ] Enter correct old password and valid new password
182+
- Expected: Success message
183+
- [ ] Log out and log in with new password
184+
- Expected: Login succeeds
185+
186+
### Registration Flow (Should Continue Working)
187+
- [ ] Register new user with weak password
188+
- Expected: Error message about password requirements
189+
- [ ] Register new user with strong password
190+
- Expected: Success, email sent (if verification enabled)
191+
192+
---
193+
194+
## Implementation Priority
195+
196+
### Phase 1: Critical Fix (Must Do Now)
197+
1. **Fix password reset form action** (5 minutes)
198+
- Change form action to `/user/savePassword`
199+
- Add `name="confirmPassword"` to match password field
200+
- Test password reset flow end-to-end
201+
202+
### Phase 2: Optional Consistency (Do Later)
203+
2. **Fix field name inconsistency** (5 minutes)
204+
- Update `update-password.js` to use `oldPassword` instead of `currentPassword`
205+
- Test update password flow
206+
207+
### Phase 3: UX Enhancement (Nice to Have)
208+
3. **Reuse password strength meter on other forms** (1-2 hours)
209+
- Extract existing meter code to shared module
210+
- Add to password reset and update password forms
211+
- Note: Registration already has this implemented
212+
213+
---
214+
215+
## Code Changes Summary
216+
217+
### Required Changes (1 file, 2 lines)
218+
```diff
219+
File: SpringUserFrameworkDemoApp/src/main/resources/templates/user/forgot-password-change.html
220+
221+
-<form id="resetPasswordForm" th:action="@{/user/resetPassword}" method="POST">
222+
+<form id="resetPasswordForm" th:action="@{/user/savePassword}" method="POST">
223+
224+
-<input type="password" id="matchPassword" class="form-control" required>
225+
+<input type="password" id="matchPassword" name="confirmPassword" class="form-control" required>
226+
```
227+
228+
### Optional Changes (1 file, 5 lines)
229+
```diff
230+
File: SpringUserFrameworkDemoApp/src/main/resources/static/js/user/update-password.js
231+
232+
const requestData = {
233+
- currentPassword: currentPassword,
234+
+ oldPassword: currentPassword,
235+
newPassword: newPassword,
236+
- confirmPassword: confirmPassword,
237+
};
238+
```
239+
240+
---
241+
242+
## Validation Reference
243+
244+
### Backend Password Policy (Configured in Framework)
245+
These rules are enforced by the backend `PasswordPolicyService`:
246+
247+
```properties
248+
user.security.password.enabled=true
249+
user.security.password.min-length=8
250+
user.security.password.max-length=128
251+
user.security.password.require-uppercase=true
252+
user.security.password.require-lowercase=true
253+
user.security.password.require-digit=true
254+
user.security.password.require-special=true
255+
user.security.password.special-chars=~`!@#$%^&*()_-+={}[]|\:;"'<>,.?/
256+
user.security.password.prevent-common-passwords=true
257+
user.security.password.history-count=3
258+
user.security.password.similarity-threshold=70
259+
```
260+
261+
### Error Messages (Now Available)
262+
New message keys added to framework:
263+
- `message.password.mismatch` - "Passwords do not match."
264+
- `message.reset-password.success` - "Your password has been successfully reset. You can now log in with your new password."
265+
266+
Existing message keys:
267+
- `TOO_SHORT`, `TOO_LONG`, `INSUFFICIENT_UPPERCASE`, etc.
268+
- `password.error.history.reuse` - "You cannot reuse your last {0} passwords."
269+
- `password.error.similarity` - "Password is too similar to your username or email ({0}% similarity)."
270+
271+
---
272+
273+
## Backward Compatibility
274+
275+
All changes are backward compatible:
276+
- ✅ Existing `/user/resetPassword` endpoint still works (sends email)
277+
- ✅ New `/user/savePassword` endpoint is additive
278+
- ✅ Existing `/user/updatePassword` endpoint enhanced with validation
279+
- ✅ No breaking changes to existing APIs
280+
- ✅ No database schema changes
281+
282+
The only "breaking" change is that the Demo App's password reset flow was already non-functional (form posted to wrong endpoint), so fixing it improves rather than breaks functionality.
283+
284+
---
285+
286+
## Questions?
287+
288+
If you have questions about these changes:
289+
1. See [IMPLEMENTATION_PLAN_PASSWORD_FIXES.md](IMPLEMENTATION_PLAN_PASSWORD_FIXES.md) for detailed context
290+
2. Review the code review findings that led to these fixes
291+
3. Test the changes in a development environment first
292+
293+
---
294+
295+
**Last Updated:** 2025-01-26
296+
**Framework Version:** 3.5.1-SNAPSHOT
297+
**Related Branch:** feature/password-validation-fixes

0 commit comments

Comments
 (0)