Package com.ericbouchut.learndev.auth
Class PasswordResetService
java.lang.Object
com.ericbouchut.learndev.auth.PasswordResetService
The password-reset ("forgot password") flow.
Security properties (see issues #53 and #55):
- Opaque, high-entropy token: 32 random bytes (256 bits) from
SecureRandom, sent as base64url in the email link. - Hashed at rest: only the SHA-256 of the token is stored; a database leak yields no usable link.
- Short-lived and single-use:
expires_atenforces the TTL,used_atenforces single use; a new request or a successful reset invalidates all other outstanding tokens. - Enumeration-safe: every outcome of a request (unknown email, rate-limited, sent) is invisible to the caller; only the audit trail differs.
- Rate-limited per user and per IP. The table has no created_at column, so recency is derived from expires_at: created > now - window equals expires_at > now - window + ttl.
-
Constructor Summary
ConstructorsConstructorDescriptionPasswordResetService(UserRepository users, PasswordResetTokenRepository tokens, org.springframework.security.crypto.password.PasswordEncoder passwordEncoder, PasswordResetMailer mailer, AuditService audit, Duration tokenTtl, long maxRequests, Duration window) -
Method Summary
Modifier and TypeMethodDescriptionfindUsableToken(String rawToken) Returns the token entity when the raw token is valid (exists, not expired, not used).voidrequestReset(String email, String ipAddress, String resetUrlBase) Handles a "forgot password" request.booleanresetPassword(String rawToken, String newPassword, String ipAddress) Consumes the token and sets the new password.
-
Constructor Details
-
PasswordResetService
public PasswordResetService(UserRepository users, PasswordResetTokenRepository tokens, org.springframework.security.crypto.password.PasswordEncoder passwordEncoder, PasswordResetMailer mailer, AuditService audit, @Value("${learndev.password-reset.token-ttl}") Duration tokenTtl, @Value("${learndev.password-reset.max-requests}") long maxRequests, @Value("${learndev.password-reset.window}") Duration window)
-
-
Method Details
-
requestReset
Handles a "forgot password" request. Always succeeds from the caller's point of view (enumeration-safe): whether the email exists, is rate-limited, or a mail was actually sent is only visible in the audit trail.- Parameters:
email- address typed in the formipAddress- requester IP (rate limiting and audit)resetUrlBase- absolute URL of the reset page, without the token, e.g.https://host/auth/reset-password
-
findUsableToken
Returns the token entity when the raw token is valid (exists, not expired, not used). Read-only: used by the GET page to decide between the form and the invalid-link message. -
resetPassword
Consumes the token and sets the new password.- Returns:
- true on success; false when the token is invalid, expired, or already used (the caller shows the invalid-link message)
-