You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
resendRegistrationToken always returns 400: it validates the full registration UserDto #361
POST /user/resendRegistrationToken can never succeed. UserAPI.resendRegistrationToken binds the same UserDto used for full registration, and that DTO carries @NotBlank on firstName, lastName, password, and matchingPassword. A resend request only has an email address to send, so validation always fails and the endpoint returns HTTP 400 without sending mail.
Found in ds-spring-user-framework:5.3.0 while verifying the demo app's Docker stacks against a real mail catcher (devondragon/SpringUserFrameworkDemoApp#87). The flow had gone unexercised because no documented demo run path had working SMTP.
Reproduce
Run the demo app with user.registration.sendVerificationEmail: true and a reachable SMTP server.
Register an account, but do not click the verification link.
Open /user/request-new-verification-email.html, enter that email, submit.
Expected: a new verification email.
Actual: the page shows "Unable to resend verification email." No mail is sent.
Server log
DEBUG ... RequestResponseBodyMethodProcessor : Read "application/json;charset=UTF-8" to
[UserDto(firstName=null, lastName=null, email=resend-check@example.com, role=null)]
WARN ... GlobalValidationExceptionHandler : Validation error occurred: Validation failed for
argument [0] in public ResponseEntity<JSONResponse> UserAPI.resendRegistrationToken(UserDto,
HttpServletRequest) with 4 errors:
[Field error in object 'userDto' on field 'matchingPassword': rejected value [null];
default message [Password confirmation is required]]
[Field error in object 'userDto' on field 'password': rejected value [null];
default message [Password is required]]
[Field error in object 'userDto' on field 'lastName': rejected value [null];
default message [Last name is required]]
[Field error in object 'userDto' on field 'firstName': rejected value [null];
default message [First name is required]]
The client is not at fault: the framework's own resend-verification.js posts { email }, which is the only field a resend needs.
Suggested fix
Bind a request type that carries just the email, e.g. a small ResendVerificationDto/EmailDto with @NotBlank @Email String email, rather than reusing the registration UserDto. POST /user/resetPassword has the same shape and is worth checking for the same problem.
Whatever the shape, the endpoint should keep its existing generic response so it does not leak whether an address is registered.
POST /user/resendRegistrationToken now binds a new ResendVerificationDto carrying only email (@NotBlank @Email @Size(max=100)) instead of the registration UserDto, so an email-only body no longer fails validation with HTTP 400.
The anti-enumeration behavior is unchanged: the same generic 200 response for unknown, already-verified, and unverified accounts, with the true outcome still recorded via audit events.
POST /user/resetPassword was checked as the issue suggested and needed no change: it already binds a dedicated PasswordResetRequestDto. Extending that check across every @RequestBody binding in src/main/java (UserAPI, GdprAPI, WebAuthnManagementAPI) found no other endpoint reusing a DTO with fields it does not need.
Test coverage added for the email-only body, blank and malformed emails (400, no mail sent), and a legacy full registration payload, which is still accepted since the extra properties are ignored rather than rejected. No client change is required.
Activity
devondragon commentedon Aug 18, 2026
PR ready for review: #362
devondragon commentedon Aug 18, 2026
Work Complete — PR Merged
PR: fix: bind email-only DTO on resendRegistrationToken (#361)
Merged into:
main(squash, commit106d011)Branch:
bugfix/361-resend-verification-email-only-dto(deleted)Summary of changes
POST /user/resendRegistrationTokennow binds a newResendVerificationDtocarrying onlyemail(@NotBlank @Email @Size(max=100)) instead of the registrationUserDto, so an email-only body no longer fails validation with HTTP 400.POST /user/resetPasswordwas checked as the issue suggested and needed no change: it already binds a dedicatedPasswordResetRequestDto. Extending that check across every@RequestBodybinding insrc/main/java(UserAPI,GdprAPI,WebAuthnManagementAPI) found no other endpoint reusing a DTO with fields it does not need.Files changed
src/main/java/com/digitalsanctuary/spring/user/dto/ResendVerificationDto.java(new)src/main/java/com/digitalsanctuary/spring/user/api/UserAPI.javasrc/test/java/com/digitalsanctuary/spring/user/api/UserAPIUnitTest.java