fix(mail): log rate-limit rejections, campus-safe code limit, multipart From name, 스팸함 안내 - #443
Merged
Merged
Conversation
Verification emails were reported as "not arriving", but 429 rejections from the signup-email/login/resend limiters were never logged, so a tripped rate limit was indistinguishable from a real delivery failure. - AuthService now logs a WARN with the limiter name and a masked client key (IP -> first two octets + "x.x"; studentId -> first 4 chars + "***") at each TOO_MANY_REQUESTS throw site. - EmailVerificationSender logs the effective SMTP host/port and a masked username once at startup so a misconfigured deploy is visible in logs instead of only surfacing as silent delivery reports.
MAX_SIGNUP_EMAIL_REQUESTS_PER_WINDOW was 5 per 10 min per client IP. Campus Wi-Fi and the club room sit behind one NAT IP, so a handful of members requesting verification codes together could trip this before any single account hit its own 1-minute resend cooldown -- indistinguishable from "email isn't arriving" without the logging just added. Raised to 20; the per-account cooldown remains the real anti-spam guard. No test previously pinned the old value of 5, so this adds direct coverage of the new cap instead of updating one.
… name
Gmail (and most inbox providers) routes a plain-text-only message with no
display name on the From address into spam/promotions far more readily
than a proper multipart message with a real sender name and a Reply-To --
the likely cause of members never seeing a code that the logs show was
sent successfully.
EmailVerificationSender now builds verification and password-reset mail
via MimeMessageHelper as multipart text+HTML, sets From to "KW COM's"
<no-reply@...> and Reply-To to the same address, and includes the code in
the subject ("[KW COM's] 이메일 인증코드 123456") alongside its existing
placement in the body. The HTML alternative is minimal: no images, no
links besides the site root. MAIL_ENABLED=false still falls back to the
existing log-only path unchanged.
Unit test mocks JavaMailSender to hand back a real MimeMessage (required
by MimeMessageHelper) and asserts both parts, the From display name, and
Reply-To.
…teps Members reporting "no email arrived" had no in-app pointer to check spam/ promotions, and the resend cooldown message wasn't visible before they tried. Both the signup email-verification step (EmailVerifyStep) and the password-reset step (Login) already have a resend action that surfaces the backend error verbatim (including the 429 cooldown message) via the shared apiClient error path -- this adds the missing hint text under each code input: "메일이 오지 않으면 스팸함·프로모션함을 확인해주세요. 1분 후 다시 요청할 수 있습니다."
Subjects surface on lock screens and inbox previews; the code belongs in the body only. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Members reported verification emails "not arriving." Investigation found: SMTP sends succeed and are logged; two Gmail recipients requested codes but never entered one (likely spam/promotions); 429 rate-limit rejections are never logged so they're indistinguishable from real delivery failures; and the per-IP email-code limit (5 per 10 min) is easily tripped by a campus NAT.
Four bounded, independently-reviewable commits:
AuthServicelogs a WARN at eachTOO_MANY_REQUESTSthrow site with the limiter name and a masked client key (IP -> first two octets +x.x; studentId -> first 4 chars +***).EmailVerificationSenderlogs the effective SMTP host/port and a masked username once at startup, so a misconfigured deploy is visible.MAX_SIGNUP_EMAIL_REQUESTS_PER_WINDOWraised 5 -> 20 per 10 min per IP; the per-account 1-minute cooldown is unchanged and remains the real anti-spam guard. No existing test pinned the old value of 5, so this adds direct coverage of the new cap.MimeMessageHelper, with aKW COM'sdisplay name on the From address, a matchingReply-To, and the code included in the subject ([KW COM's] 이메일 인증코드 123456). HTML is minimal: no images, no links besides the site root.MAIL_ENABLED=falsestill falls back to the existing log-only path.apiClienterror path.Test plan
./gradlew test— BUILD SUCCESSFUL (full suite, incl. newEmailVerificationSenderTestand the new signup-email rate-limit test)npm test— typecheck, lint, and all 26 contract tests pass🤖 Generated with Claude Code