From 19e855d35fd122a3165b2ec09c32ab889b7e3033 Mon Sep 17 00:00:00 2001 From: ICantThinkOfAName-tech Date: Fri, 16 Oct 2026 21:45:37 +0200 Subject: [PATCH] Send the Send verification email in the background Sending the code only for allowed emails made those responses take as long as the SMTP round trip, which tells allowed and unknown emails apart. Mail the code in the background so both answer the same way. Co-Authored-By: Claude Opus 5.5 --- src/auth/send.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/auth/send.rs b/src/auth/send.rs index 0b57f38a..d4da2470 100644 --- a/src/auth/send.rs +++ b/src/auth/send.rs @@ -156,12 +156,14 @@ impl SendTokens { let Some(code) = otp.map(|o| o.trim().to_owned()).filter(|o| !o.is_empty()) else { if stored.as_ref().is_none_or(SendAccessOtp::can_resend) { let code = crypto::generate_email_token(CONFIG.email_token_size()); - let new_otp = SendAccessOtp::new(send.uuid.clone(), email.clone(), code.clone()); - new_otp.save(conn).await?; - if let Err(e) = mail::send_send_access_otp(&email, &code).await { - new_otp.delete(conn).await.ok(); - return Err(e); - } + SendAccessOtp::new(send.uuid.clone(), email.clone(), code.clone()).save(conn).await?; + // Mail in the background, so neither the response time nor a mail failure tells listed + // and unlisted emails apart. On failure the recipient can request a new code after the cooldown. + tokio::spawn(async move { + if let Err(e) = mail::send_send_access_otp(&email, &code).await { + error!("Failed to send Send verification code: {e:#?}"); + } + }); } return Self::expected_error("Email and OTP required", "email_and_otp_required"); };