fix(email): reword password-reset email + show request country (BUNYIP-397) #389
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/BUNYIP-397-reset-email-copy"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What
Reword the password-reset email so it reads as a routine notification instead of "Someone asked to reset the password for your account" (which sounds like an intrusion), and show the country the request came from when the IP resolves.
Before: "Someone asked to reset the password for your {app} account. Click below to choose a new one."
After: "A password reset was requested for your {app} account (from United States). If this was you, choose a new password below. If it wasn't, you can ignore this email and nothing changes."
How
GeoIpService::country_name(ip): the IP2Location LITE DB already stores the long country name alongside the code, so this needs no new dependency. Mirrorscountry_code's"-"/blank normalization.AuthService::country_name_for_ip(ip): the same best-effort, privacy-preserving filter the login-location signal uses (GeoIP present, IP present, public IP only), yieldingNoneotherwise.request_password_resethandler: resolves the country from the already-captured client IP and threads it intosend_password_reset.EmailService::send_password_reset(email, token, country: Option<&str>): inserts the country as an empty string when absent, so the Tera{% if country %}guard hides the "(from ...)" clause.admin.rs): passesNone(no requester IP).password_reset.htmlandpassword_reset.txtreworded.Scope / decisions (with Nate)
Country-level only (GeoIP resolves country, not city). When the location is unknown (admin reset, local IP, or
IP2LOCATION_DB_PATHunset) the email omits the "(from ...)" clause and reads correctly. The login-location alert email keeps its terser country code; unifying that is out of scope.Tests
GeoIpServicegains anormalize_country_nameunit test (placeholder/blank rejection, trimming). No new SQL, so the committed.sqlxcache is unchanged.cargo clippy --workspace --all-targets -- -D warnings,cargo fmt --all --check, andcargo test --workspace --libpass.#BUNYIP-397
The reset email opened with "Someone asked to reset the password for your account", which reads as alarming rather than as a routine notification. Reword it to "A password reset was requested for your account ... If this was you, choose a new password below. If it wasn't, you can ignore this email and nothing changes.", and show the country the request came from when the IP resolves. Bunyip already resolves an IP to a country for login-location alerts and the reset handler already captures the client IP, so this reuses both: GeoIpService gains country_name (the IP2Location LITE DB carries the long country name, so no new dependency), AuthService gains country_name_for_ip (the same GeoIP-present / public-IP filter the login signal uses), and the handler threads the resolved country into send_password_reset. The name is inserted as an empty string when absent, so the Tera "{% if country %}" guard hides the "(from ...)" clause for admin-initiated resets, local IPs, or when IP2LOCATION_DB_PATH is unset. Country-level only (GeoIP has no city). No new SQL, so the .sqlx offline cache is unchanged. #BUNYIP-397 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>