Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/vanegasjoseignacio2-cyber/Eco-It/llms.txt

Use this file to discover all available pages before exploring further.

Eco-It’s password recovery flow gives users a safe way to regain access to their account when they have forgotten their password. Recovery does not require the user to be logged in. A 6-digit code is delivered by email, verified server-side using a SHA-256 hash comparison, and then used to authorise a password reset. The new password is set in plain text and hashed automatically by the User model’s bcrypt pre('save') hook. The entire flow must be completed before the code expires.

Recovery flow

1

Request a recovery code

Submit the account’s email address. If a matching user is found, the backend generates a 6-digit code, hashes it with SHA-256, and stores the hash alongside an expiry timestamp on the User document (resetPasswordToken and resetPasswordExpires). The plain-text code is emailed to the user.Endpoint: POST /api/auth/recuperar-password
email
string
required
The email address of the account to recover.
curl -X POST https://api.eco-it.co/api/auth/recuperar-password \
  -H "Content-Type: application/json" \
  -d '{ "email": "laura@example.com" }'
Success response 200:
{
  "success": true,
  "mensaje": "Código enviado a tu correo electrónico"
}
Error response 404 — email not registered:
{
  "success": false,
  "mensaje": "No existe un usuario con ese email"
}
For security, in a production hardening pass you may want to return a generic 200 regardless of whether the email exists, to prevent user-enumeration attacks. The current implementation returns 404 if the email is not found.
2

Verify the code

Submit the email and the 6-digit code from the email. The backend hashes the submitted code and looks for a User document where resetPasswordToken matches the hash and resetPasswordExpires is still in the future. A 200 response confirms the code is valid and the user may proceed to set a new password.Endpoint: POST /api/auth/recuperar-password/verificar
email
string
required
The account’s email address.
codigo
string
required
The 6-digit code received by email.
curl -X POST https://api.eco-it.co/api/auth/recuperar-password/verificar \
  -H "Content-Type: application/json" \
  -d '{
    "email": "laura@example.com",
    "codigo": "749203"
  }'
Success response 200:
{
  "success": true,
  "mensaje": "Código válido"
}
Error response 400 — invalid or expired code:
{
  "success": false,
  "mensaje": "Código inválido o expirado"
}
3

Set a new password

Submit the email, the verified code, and the new password in a single request. The backend re-validates the code (repeating the hash lookup with the expiry check) to ensure the reset token has not expired between step 2 and step 3. On success, resetPasswordToken and resetPasswordExpires are cleared from the user document and the new password is saved in plain text — the pre('save') hook hashes it automatically.Endpoint: POST /api/auth/recuperar-password/restablecer
email
string
required
The account’s email address.
codigo
string
required
The same 6-digit code used in step 2.
password
string
required
The new plain-text password. Must meet the schema minimum of 8 characters.
curl -X POST https://api.eco-it.co/api/auth/recuperar-password/restablecer \
  -H "Content-Type: application/json" \
  -d '{
    "email": "laura@example.com",
    "codigo": "749203",
    "password": "NuevaClave2024!"
  }'
Success response 200:
{
  "success": true,
  "mensaje": "Contraseña actualizada correctamente"
}
Error response 400 — code expired or already used:
{
  "success": false,
  "mensaje": "Código inválido o expirado"
}

Resending the code

If the recovery email was not received or the code has expired, users can request a new one without restarting the flow. A 3-minute cooldown applies between resend requests for the same email address: if the current code has more than 12 minutes of its 15-minute lifetime remaining (meaning fewer than 3 minutes have passed since it was issued), the server returns 429 with the number of minutes to wait. Endpoint: POST /api/auth/recuperar-password/reenviar
email
string
required
The account’s email address.
curl -X POST https://api.eco-it.co/api/auth/recuperar-password/reenviar \
  -H "Content-Type: application/json" \
  -d '{ "email": "laura@example.com" }'
Success response 200:
{
  "success": true,
  "mensaje": "Nuevo código enviado a tu correo electrónico"
}
Cooldown response 429:
{
  "success": false,
  "mensaje": "Por favor espera 2 minuto(s) antes de solicitar otro código."
}
Issuing a new code via the resend endpoint invalidates the previous code immediately. The new resetPasswordToken hash overwrites the old one on the User document, so any previously received code will fail validation at step 2 or step 3.

Frontend flow

The recovery journey maps to three distinct React pages, protected by a RecoveryRoute guard that ensures users cannot jump directly to a later step without completing the earlier ones.
PageRoutePurpose
RecuperarPage/recuperar-passwordCollects the email and calls step 1.
VerificarCode/recuperar-password/verificarCollects the 6-digit code and calls step 2.
New password form/recuperar-password/restablecerCollects the new password and calls step 3.
RecoveryRoute checks for a recovery session state (set in React Router’s location state after step 1 succeeds). Attempting to navigate directly to /recuperar-password/verificar without the session state redirects back to /recuperar-password.

Rate limiting

All four recovery endpoints — POST /api/auth/recuperar-password, POST /api/auth/recuperar-password/verificar, POST /api/auth/recuperar-password/reenviar, and POST /api/auth/recuperar-password/restablecer — are protected by authLimiter, which allows a maximum of 15 requests per IP in a 15-minute window. This is 20× more restrictive than the global API limiter (300 requests per window).

Code security

Recovery codes are never stored in plain text. The flow uses a SHA-256 hash both in the PendingRegistration (registration) and on the User model (recovery):
// From backend/controllers/recoveryController.js
const resetToken = Math.floor(100000 + Math.random() * 900000).toString();
const resetTokenHash = crypto.createHash('sha256').update(resetToken).digest('hex');

// Email is sent first; token is only persisted if delivery succeeds
await sendRecoveryEmail(email, resetToken);  // plain-text code emailed, never stored

user.resetPasswordToken  = resetTokenHash;  // stored in DB
user.resetPasswordExpires = Date.now() + 15 * 60 * 1000;  // 15 minutes
await user.save();
On verification, the submitted code is hashed before comparison, so the plain-text code is never compared directly against stored data.
Codes expire after 15 minutes. If a user waits longer than 15 minutes before completing step 2 or step 3, the verification will fail with “Código inválido o expirado”. They must request a fresh code via POST /api/auth/recuperar-password and restart the flow.

Build docs developers (and LLMs) love