Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/JuanSebasSV/healtyhelp/llms.txt

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

Overview

HealtyHelp protects every user session with JSON Web Tokens (JWT). Two authentication methods are available: classic email/password with mandatory email verification, and Google OAuth 2.0 via Passport.js. Both flows issue the same JWT token format, so the rest of the API treats them identically.

Authentication Methods

MethodFlowVerification required
Email / PasswordRegister → verify email → loginYes — 6-digit code
Google OAuthClick “Continue with Google” → Google consent → JWT redirectNo — Google already verified the email

JWT Token Flow

When a user registers (after email verification) or logs in successfully, the server signs a JWT with the configured secret and expiry:
const generateToken = (id) =>
  jwt.sign({ id }, process.env.JWT_SECRET, {
    expiresIn: process.env.JWT_EXPIRE,
  });
The token is returned in the JSON response body:
{
  "success": true,
  "token": "<JWT>",
  "user": { ... }
}

Using the token

Include the token in every authenticated request as a Bearer token in the Authorization header:
Authorization: Bearer <your-jwt-token>
Tokens must be stored client-side (e.g., localStorage) and sent with every request to a protected endpoint.

The protect Middleware

All private routes run through the protect middleware before reaching their handler:
// server/middleware/auth.js
exports.protect = async (req, res, next) => {
  let token;
  if (req.headers.authorization?.startsWith('Bearer')) {
    token = req.headers.authorization.split(' ')[1];
  }
  if (!token) {
    return res.status(401).json({ error: 'No autorizado - Token no proporcionado' });
  }
  const decoded = jwt.verify(token, process.env.JWT_SECRET);
  // Always load a fresh copy of the user from the database
  const user = await User.findById(decoded.id).select('-password');
  if (!user) {
    return res.status(401).json({ error: 'Usuario no existe o fue eliminado' });
  }
  req.user = user;
  next();
};
Key behaviour:
  • Rejects requests with no token (401).
  • Verifies the signature using JWT_SECRET.
  • Always fetches a fresh user record from MongoDB — deleted or modified accounts are caught immediately.
  • Returns 401 for expired or tampered tokens.

The admin Middleware

Routes that require administrator access chain protect then admin:
exports.admin = async (req, res, next) => {
  const user = await User.findById(req.user._id).select('-password');
  if (!user || user.role !== 'admin') {
    return res.status(403).json({
      error: 'Acceso denegado - Requiere privilegios de administrador',
    });
  }
  req.user = user; // attach the freshly loaded user
  next();
};
The middleware re-queries the database so that role changes take effect without requiring a new token.

Roles System

HealtyHelp has three tiers of privilege, stored directly on the User document:
Field / ValueDescription
role: 'user'Standard authenticated user (default)
role: 'admin'Platform administrator — full management access
isSuperAdmin: trueSuper-administrator flag; the account cannot be deleted
The restrictTo(...roles) helper is available for fine-grained route protection:
// Only allow admins
router.get('/admin/users', protect, admin, handler);

// Restrict to specific roles programmatically
router.delete('/resource', protect, restrictTo('admin'), handler);

Password Requirements

All passwords — whether set at registration or when a Google user adds a password — are validated against these rules:
  • Minimum 8 characters
  • At least one lowercase letter (a–z)
  • At least one uppercase letter (A–Z)
  • At least one digit (0–9)
Passwords are hashed with bcryptjs (12 salt rounds) before being stored. The password field is excluded from all query results by default (select: false).

Account Lockout

HealtyHelp automatically locks an account after 5 consecutive failed login attempts:
loginAttempts: { type: Number, default: 0 }
lockUntil:     Date
  • Each failed attempt increments loginAttempts via user.incLoginAttempts().
  • Once loginAttempts >= 5, lockUntil is set to 15 minutes in the future.
  • While locked, the API returns 423 Locked with the remaining wait time in minutes.
  • A lockout warning email is sent to the account owner.
  • On successful login, loginAttempts and lockUntil are reset via user.resetLoginAttempts().
  • Completing a password reset also clears the lockout state.
The /api/auth/login endpoint is rate-limited to 20 requests per 15-minute window in production. Exceeding this limit returns a 429 Too Many Requests response — regardless of whether credentials are correct.

Ban System

Administrators can ban users with time-limited or permanent bans. The ban state is recorded on the user document:
FieldTypeDescription
baneadoBooleanWhether the account is currently banned
baneadoHastaDateBan expiry (null = permanent)
baneadoMotivoStringHuman-readable reason for the ban
baneadoPorObjectIdReference to the admin who issued the ban
baneadoEnDateTimestamp when the ban was applied

Session Preferences

Users can configure automatic session expiry. These preferences are stored per-user and respected by the frontend:
FieldTypeDefaultConstraints
autoLogoutEnabledBooleanfalse
autoLogoutMinutesNumber151 – 480 minutes
Update session preferences via:
PATCH /api/auth/preferences
Authorization: Bearer <token>
Content-Type: application/json

{
  "autoLogoutEnabled": true,
  "autoLogoutMinutes": 30
}

Environment Variables

VariableDescription
JWT_SECRETSecret key used to sign all tokens
JWT_EXPIREToken lifetime (e.g. "7d", "24h")
See the Google OAuth page for OAuth-specific variables and the Email Verification page for mail service variables.

Build docs developers (and LLMs) love