Avisar al correo antiguo cuando el cambio de correo se completa

`oldEmailNotificationHtml` estaba portada del Django original pero NO la llamaba
nadie: `confirmNewEmail` cambiaba el correo de la cuenta y al antiguo no le
llegaba nada. Importa porque al entrar se usa el correo: quien secuestrara una
sesión podía cambiarlo y el dueño se quedaba sin cuenta en silencio, sin ningún
aviso en ninguna parte.

Se envía DESPUÉS de que el cambio haya cuajado (si el UPDATE falla, no hay nada
que avisar) y sin enlaces: solo informa, y dice a quién contactar. `sendMail`
nunca lanza (devuelve false), así que un fallo de correo no puede tumbar un
cambio ya hecho.

Verificado ejecutando confirmNewEmail() contra la BD con el SMTP interceptado y
cuentas de usar y tirar: 1 correo al antiguo (antes 0), la cuenta cambia, y las
filas de prueba quedan borradas.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-15 19:32:10 +00:00
parent 4303e9fc88
commit fbc17f2cf8
+15 -2
View File
@@ -5,7 +5,7 @@ import { authenticate } from './auth'
import { bnetMakeRegistration, normalizeEmail, EMAIL_RE } from './bnet'
import { checkSecurityToken } from './security-token'
import { sendMail } from './mail'
import { confirmNewEmailHtml, confirmOldEmailHtml } from './emails'
import { confirmNewEmailHtml, confirmOldEmailHtml, oldEmailNotificationHtml } from './emails'
import type { SessionData } from './session'
@@ -71,7 +71,7 @@ export async function confirmOldEmail(hash: string): Promise<Result> {
export async function confirmNewEmail(hash: string): Promise<Result> {
const [rows] = await db(DB.default).query<RowDataPacket[]>(
'SELECT id, email, old_email, password FROM accountactivation WHERE hash = ? AND is_new_email_used = 0 AND old_email IS NOT NULL',
'SELECT id, email, old_email, password, username FROM accountactivation WHERE hash = ? AND is_new_email_used = 0 AND old_email IS NOT NULL',
[hash],
)
const act = rows[0]
@@ -100,6 +100,19 @@ export async function confirmNewEmail(hash: string): Promise<Result> {
} catch {
return { success: false, error: 'genericError' }
}
// Avisar al correo ANTIGUO de que ya no manda en la cuenta. Va después de que el
// cambio haya cuajado (si falla arriba, no hay nada que avisar) y sin enlaces:
// solo informa. Si alguien secuestra una sesión y cambia el correo, esta es la
// única forma que tiene el dueño de enterarse, porque al entrar se usa el correo.
// `sendMail` nunca lanza (devuelve false), así que no puede tumbar un cambio ya
// hecho: como mucho el aviso no sale.
await sendMail(
oldNorm,
`Cambio de correo realizado - NightSpire`,
oldEmailNotificationHtml(oldNorm, act.username ?? null, newNorm),
)
await db(DB.default).query('DELETE FROM accountactivation WHERE id = ?', [act.id])
return { success: true }
}