From 75e4ceed4c062f2c37196a4bc57f4352fec14bdc Mon Sep 17 00:00:00 2001 From: adevopg Date: Wed, 15 Jul 2026 17:03:39 +0000 Subject: [PATCH] El token de seguridad caducado ya no vale; traducir 2 errores que faltaban MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit checkSecurityToken guardaba expires_at pero NO lo comprobaba: un token caducado seguía siendo válido para regalos y servicios. Se comprueba en SQL (`expires_at > NOW()`) y no en JS, para que los dos lados de la comparación sean hora del servidor: expires_at se inserta como Date de JS y compararlo contra Date.now() dependería de que Node y MySQL tuvieran la misma zona. Además faltaban `errors.notAuthenticated` e `invalidRequest` en el namespace Store (ES y EN): si se caducaba la sesión, send-gift decía «Ha ocurrido un error» en vez de pedir que vuelvas a iniciar sesión — justo el mensaje que hace pensar que el botón sigue roto. Co-Authored-By: Claude Opus 4.8 (1M context) --- web-next/lib/security-token.ts | 13 ++++++++++--- web-next/messages/en.json | 4 +++- web-next/messages/es.json | 4 +++- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/web-next/lib/security-token.ts b/web-next/lib/security-token.ts index b45348a..ef18c2a 100644 --- a/web-next/lib/security-token.ts +++ b/web-next/lib/security-token.ts @@ -45,11 +45,18 @@ export async function requestSecurityToken(session: SessionData, ip: string): Pr return { success: true, tokenDate: created[0] ? new Date(created[0].created_at).toISOString() : undefined } } -/** Comprueba que el token coincide con el de la cuenta. */ +/** + * Comprueba que el token coincide con el de la cuenta y NO ha caducado. + * + * La caducidad se evalúa en SQL (`expires_at > NOW()`) a propósito: `expires_at` + * se inserta como Date de JS y se guarda en la zona horaria del servidor, así que + * compararlo aquí contra `Date.now()` dependería de que Node y MySQL coincidan. + * Comparando dentro de MySQL, ambos lados son hora del servidor. + */ export async function checkSecurityToken(userId: number, token: string): Promise { const [rows] = await db(DB.default).query( - 'SELECT token FROM securitytoken WHERE user_id = ? ORDER BY created_at DESC LIMIT 1', + 'SELECT token, expires_at > NOW() AS valid FROM securitytoken WHERE user_id = ? ORDER BY created_at DESC LIMIT 1', [userId], ) - return Boolean(rows[0] && rows[0].token === token) + return Boolean(rows[0] && rows[0].token === token && rows[0].valid) } diff --git a/web-next/messages/en.json b/web-next/messages/en.json index 71332c1..a5ffdaf 100644 --- a/web-next/messages/en.json +++ b/web-next/messages/en.json @@ -1949,7 +1949,9 @@ "invalidDestination": "The target character name is not valid.", "invalidSource": "The source character does not belong to your account.", "destinationNotFound": "The target character does not exist.", - "invalidToken": "The security token is not valid." + "invalidToken": "The security token is not valid.", + "notAuthenticated": "Your session has expired. Please log in again to continue.", + "invalidRequest": "Invalid request." }, "newsTitle": "News", "newsIntro": "List of additions made based on the community's suggestions in our forum.", diff --git a/web-next/messages/es.json b/web-next/messages/es.json index b582610..1951901 100644 --- a/web-next/messages/es.json +++ b/web-next/messages/es.json @@ -1949,7 +1949,9 @@ "invalidDestination": "El nombre del personaje de destino no es válido.", "invalidSource": "El personaje de origen no pertenece a tu cuenta.", "destinationNotFound": "El personaje de destino no existe.", - "invalidToken": "El token de seguridad no es válido." + "invalidToken": "El token de seguridad no es válido.", + "notAuthenticated": "Tu sesión ha caducado. Vuelve a iniciar sesión para continuar.", + "invalidRequest": "Solicitud no válida." }, "newsTitle": "Novedades", "newsIntro": "Lista de adiciones realizadas en base a las sugerencias de la comunidad en nuestro foro.",