|
| 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