El enlace de activación vuelve a ser alfanumérico; avisar a qué correo se envió

Dos cosas que el port de Django cambió sin querer:

- El hash de activación se generaba con `randomBytes(16).toString('hex')`: 32
  caracteres, sí, pero solo `0-9a-f`. El original era `get_random_string(32)`,
  alfanumérico con mayúsculas y minúsculas (`?act=P4JHQDJey2jJDnEBnqUePI5O2qEFB1`).
- El aviso de cuenta creada era un genérico «Revisa tu correo», mientras que el
  original decía qué cuenta se creó y a qué dirección fue el enlace. Se recupera
  el formato de dos líneas (con id `create-response`, como el original).

El muestreo por rechazo del token de seguridad se sube a `lib/random-token.ts` y
lo comparten los dos generadores, que solo se diferencian en el alfabeto: letras
para el token que se teclea a mano, alfanumérico para el hash de la URL.

Verificado con 100.000 hashes: todos casan /^[A-Za-z0-9]{32}$/, salen los 62
caracteres y la desviación por carácter se queda en 1,3% (ruido).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-15 17:34:53 +00:00
parent 6e4a123dde
commit 8c85cf2dbe
6 changed files with 58 additions and 30 deletions
@@ -63,7 +63,9 @@ export function RegisterForm() {
const [busy, setBusy] = useState(false)
const [captcha, setCaptcha] = useState('')
const [captchaKey, setCaptchaKey] = useState(0) // remonta el widget para pedir un token nuevo
const [message, setMessage] = useState<{ ok: boolean; text: string } | null>(null)
// `extra` es la segunda línea del aviso de éxito (a qué correo fue el enlace);
// los errores solo usan `text`.
const [message, setMessage] = useState<{ ok: boolean; text: string; extra?: string } | null>(null)
const canSubmit = accepted && notUs && !busy
@@ -99,7 +101,10 @@ export function RegisterForm() {
})
const data: { success?: boolean; error?: string } = await res.json()
if (data.success) {
setMessage({ ok: true, text: t('success') })
// La cuenta ES el correo (Battle.net), así que el enlace de activación va
// justo a la dirección con la que se registró: mismo valor en las 2 líneas.
const email = form.email.trim()
setMessage({ ok: true, text: t('success', { email }), extra: t('successActivation', { email }) })
} else {
const key = (ERROR_KEYS as readonly string[]).includes(data.error ?? '') ? data.error! : 'genericError'
setMessage({ ok: false, text: t(key) })
@@ -200,8 +205,15 @@ export function RegisterForm() {
</table>
</form>
<hr />
<div className="alert-message" style={{ display: message ? 'block' : 'none' }}>
<div className="alert-message" id="create-response" style={{ display: message ? 'block' : 'none' }}>
{message && <span className={message.ok ? 'ok-form-response' : 'red-form-response'}>{message.text}</span>}
{message?.extra && (
<>
<br />
<br />
<span>{message.extra}</span>
</>
)}
</div>
</>
)