Skip to content

Conversation

@devondragon
Copy link
Owner

This pull request introduces several important changes to improve password validation and security in the SpringUserFramework, especially around password reset and update flows. The main updates include adding a dedicated endpoint for saving new passwords after a reset, enforcing password policy and history checks, improving error handling and messaging, and enhancing the demo application's forms for better usability and consistency.

Key changes are grouped below:

Password Reset Flow Improvements

  • Added a new /user/savePassword endpoint in UserAPI.java to handle saving a new password after a password reset token is validated. This endpoint checks for password match, validates the reset token, enforces password policy (including history and strength), saves the new password, and deletes the token after use.
  • Introduced SavePasswordDto as a new DTO to carry the token, new password, and confirmation password for the password reset flow.
  • Implemented deletePasswordResetToken in UserService.java to remove used tokens and prevent reuse.

Password Policy and History Enforcement

  • Updated password policy enforcement to clarify that history checks are only applied to existing users (not during registration), and improved JavaDoc comments for clarity. [1] [2]
  • Made the password history relation in User entity explicit and set to LAZY loading. Also, changed the fetch type for PasswordHistoryEntry.user to LAZY for efficiency. [1] [2]
  • Added a @Transactional(isolation = Isolation.SERIALIZABLE) annotation to cleanUpPasswordHistory to prevent race conditions when updating password history.

Demo Application and User Experience

  • Provided a comprehensive guide (DEMO_APP_CHANGES_REQUIRED.md) outlining necessary changes in the demo app, including fixing the password reset form to use the correct endpoint and field names, and suggestions for reusing the password strength meter across all password-related forms.

Error Handling and Messaging

  • Added new localized error messages for password mismatch and reset success in dsspringusermessages.properties.
  • Improved error handling and logging throughout the password reset and update flows, including audit logging for both success and failure cases. [1] [2]

References:
[1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11]

devondragon and others added 2 commits October 26, 2025 12:36
This commit addresses 4 critical security issues in password management:

**Fix #1: Add password validation to /updatePassword endpoint**
- Added passwordPolicyService.validate() call before saving password
- Prevents users from setting weak passwords when updating
- Enforces all policy rules including history, similarity, and complexity

**Fix #2: Implement missing /savePassword endpoint**
- Created SavePasswordDto for password reset with token
- Implemented complete /savePassword endpoint with full validation
- Added deletePasswordResetToken method to UserService
- Added message keys for password reset flow
- Fixes incomplete password reset functionality

**Fix #3: Document password history behavior**
- Added JavaDoc explaining null user parameter during registration
- Documented that history checks only apply to existing users
- Added clarifying comments in registration endpoint

**Fix #4: Add transaction isolation for password history cleanup**
- Added @transactional(isolation = SERIALIZABLE) to cleanUpPasswordHistory
- Prevents race conditions during concurrent password changes
- Added comprehensive JavaDoc documentation

**Additional improvements:**
- Cascade delete configuration for PasswordHistoryEntry (from earlier work)
- LAZY fetch type optimization on both sides of relationship
- All existing tests pass (372 tests)

See IMPLEMENTATION_PLAN_PASSWORD_FIXES.md for detailed analysis and implementation plan.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Updated DEMO_APP_CHANGES_REQUIRED.md to reflect that:
- Password strength meter already exists on registration form
- Can be extracted and reused on password reset and update forms
- Reduced estimated effort from 2-4 hours to 1-2 hours

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@devondragon devondragon self-assigned this Oct 26, 2025
@devondragon devondragon added enhancement New feature or request security labels Oct 26, 2025
Copilot AI review requested due to automatic review settings October 26, 2025 18:48
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This pull request implements comprehensive security improvements to the password reset and update flows, addressing critical validation gaps and enforcing password policies consistently throughout the user authentication system. The changes introduce a new dedicated password reset endpoint, enhance password policy enforcement with history checking, improve database performance through optimized lazy loading, and provide clear documentation for required demo application updates.

Key Changes:

  • Added /user/savePassword endpoint with full password policy validation and token management for the password reset flow
  • Enhanced password update endpoint to validate new passwords against policy rules including history checking
  • Optimized password history relationship loading and added transaction isolation to prevent race conditions

Reviewed Changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
SavePasswordDto.java New DTO to support password reset flow with token and password fields
UserAPI.java Added savePassword endpoint and enhanced updatePassword with policy validation
UserService.java Added token deletion method and SERIALIZABLE isolation for history cleanup
PasswordPolicyService.java Clarified documentation that history checks skip new user registrations
User.java Added explicit LAZY-loaded password history relationship
PasswordHistoryEntry.java Changed user relationship to LAZY loading for performance
dsspringusermessages.properties Added password mismatch and reset success messages
IMPLEMENTATION_PLAN_PASSWORD_FIXES.md Comprehensive implementation guide and testing strategy
DEMO_APP_CHANGES_REQUIRED.md Documentation of required demo app form fixes
Comments suppressed due to low confidence (1)

src/main/java/com/digitalsanctuary/spring/user/dto/SavePasswordDto.java:1

  • The @NotEmpty annotation already validates for null and empty strings, making @NotNull redundant. Consider removing @NotNull annotations or use @NotBlank which is more appropriate for trimming whitespace in string fields.
package com.digitalsanctuary.spring.user.dto;

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 421 to 425
PasswordResetToken resetToken = passwordTokenRepository.findByToken(token);
if (resetToken != null) {
passwordTokenRepository.delete(resetToken);
log.debug("Deleted password reset token for user: {}", resetToken.getUser().getEmail());
}
Copy link

Copilot AI Oct 26, 2025

Choose a reason for hiding this comment

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

The method fetches the entire PasswordResetToken entity just to delete it. Consider adding a deleteByToken(String token) method to the repository to execute a direct DELETE query, avoiding the unnecessary SELECT and reducing database roundtrips.

Suggested change
PasswordResetToken resetToken = passwordTokenRepository.findByToken(token);
if (resetToken != null) {
passwordTokenRepository.delete(resetToken);
log.debug("Deleted password reset token for user: {}", resetToken.getUser().getEmail());
}
passwordTokenRepository.deleteByToken(token);
log.debug("Deleted password reset token with token: {}", token);

Copilot uses AI. Check for mistakes.
Copy link
Owner Author

Choose a reason for hiding this comment

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

Will fix.

…parison

This commit addresses code review feedback from Copilot AI:

**1. Clarify password comparison timing attack concern**
- Added detailed comment explaining why equals() is safe for comparing
  two user-provided strings (newPassword vs confirmPassword)
- Timing attacks only matter when comparing against stored secrets
- Spring's PasswordEncoder already uses constant-time comparison for
  actual credential verification

**2. Optimize password reset token deletion**
- Added @Modifying query method deleteByToken() to PasswordResetTokenRepository
- Uses direct DELETE query instead of SELECT + DELETE
- Reduces database roundtrips from 2 to 1
- Updated UserService.deletePasswordResetToken() to use optimized method
- Maintains logging while improving performance

All tests pass (372 tests).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@devondragon devondragon merged commit 5e2ad57 into main Oct 26, 2025
3 checks passed
@github-project-automation github-project-automation bot moved this from In progress to Done in SpringUserFramework Oct 26, 2025
@devondragon devondragon deleted the feature/password-validation-fixes branch October 26, 2025 18:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request security

Projects

Development

Successfully merging this pull request may close these issues.

2 participants