Skip to main content

Overview

CUIDO Backend implements a secure authentication system using JSON Web Tokens (JWT) with role-based access control. The system supports three user roles: user, admin, and system.

Authentication Flow

JWT Token Generation

Tokens are generated using the authService:
// src/services/authService.js
import jwt from 'jsonwebtoken';

class AuthService {
  generateToken(userId) {
    return jwt.sign(
      { userId, timestamp: Date.now() },
      process.env.JWT_SECRET,
      { 
        expiresIn: process.env.JWT_EXPIRES_IN || '7d',
        issuer: 'claude-prompt-api',
        subject: userId.toString()
      }
    );
  }
}
Default token expiration: 7 days. Configure via JWT_EXPIRES_IN environment variable.

Token Verification

Token validation with comprehensive error handling:
verifyToken(token) {
  try {
    return jwt.verify(token, process.env.JWT_SECRET);
  } catch (error) {
    if (error.name === 'TokenExpiredError') {
      throw new AppError('Token expirado', 401);
    }
    if (error.name === 'JsonWebTokenError') {
      throw new AppError('Token inválido', 401);
    }
    throw new AppError('Error de autenticación', 401);
  }
}

User Registration

Secure user registration with email validation:
async register({ name, email, password }) {
  const existingUser = await User.findOne({ email: email.toLowerCase() });
  if (existingUser) {
    throw new AppError('El email ya está registrado', 400);
  }

  const user = new User({ name, email: email.toLowerCase(), password });
  await user.save();

  const token = this.generateToken(user._id);
  
  return {
    user: user.toJSON(),
    token
  };
}
Passwords are automatically hashed using bcrypt with a salt rounds of 12 before saving to the database.

User Login

Authenticate users and track login activity:
async login({ email, password }) {
  const user = await User.findOne({ 
    email: email.toLowerCase(),
    isActive: true 
  }).select('+password');

  if (!user || !(await user.comparePassword(password))) {
    throw new AppError('Credenciales inválidas', 401);
  }

  // Update last login timestamp
  user.lastLogin = new Date();
  await user.save();

  const token = this.generateToken(user._id);
  
  return {
    user: user.toJSON(),
    token
  };
}

Authentication Middleware

Protect routes with the authenticate middleware:
// src/middleware/auth.js
import authService from '../services/authService.js';
import { asyncHandler } from '../utils/response.js';

export const authenticate = asyncHandler(async (req, res, next) => {
  const authHeader = req.headers.authorization;

  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    return res.status(401).json({
      success: false,
      message: 'Token de acceso requerido'
    });
  }

  const token = authHeader.substring(7);

  let decoded;
  try {
    decoded = authService.verifyToken(token);
  } catch (error) {
    return res.status(401).json({
      success: false,
      message: 'Token inválido o expirado'
    });
  }

  const user = await authService.getCurrentUser(decoded.userId);

  if (!user) {
    return res.status(401).json({
      success: false,
      message: 'Usuario no encontrado'
    });
  }

  req.user = user;
  req.userId = user._id;
  next();
});

Usage in Routes

import { authenticate } from '../middleware/auth.js';

const router = express.Router();

// All routes below require authentication
router.use(authenticate);

router.post('/survey/quick', processQuickSurvey);
router.get('/wellness-heatmap/:hospitalId', getWellnessHeatmap);

Role-Based Authorization

The authorize middleware restricts access based on user roles:
export const authorize = (...roles) => {
  return (req, res, next) => {
    if (!roles.includes(req.user.role)) {
      return res.status(403).json({
        success: false,
        message: 'No tienes permisos para acceder a este recurso'
      });
    }
    next();
  };
};

Authorization Examples

// Only administrators can access
router.get('/alerts', 
  authenticate, 
  authorize('admin'), 
  getAlerts
);

User Roles

User

Standard access level for healthcare employees
  • Complete surveys
  • Access mentorship
  • View own stats
  • Enroll in courses

Admin

Administrative access for HR and managers
  • View all reports
  • Manage alerts
  • Grant recognition
  • Access analytics

System

System-level access for automated processes
  • Run scheduled tasks
  • Generate reports
  • Process bulk data

User Model

The User schema with built-in security features:
// src/models/User.js
import mongoose from 'mongoose';
import bcrypt from 'bcryptjs';

const userSchema = new mongoose.Schema({
  email: {
    type: String,
    required: [true, 'El email es requerido'],
    unique: true,
    lowercase: true,
    trim: true,
    match: [/^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,3})+$/, 'Email inválido']
  },
  password: {
    type: String,
    required: [true, 'La contraseña es requerida'],
    minlength: [6, 'La contraseña debe tener al menos 6 caracteres'],
    select: false  // Don't include in queries by default
  },
  name: {
    type: String,
    required: [true, 'El nombre es requerido'],
    trim: true,
    maxlength: [50, 'El nombre no puede exceder 50 caracteres']
  },
  role: {
    type: String,
    enum: ['user', 'admin', 'system'], 
    default: 'user'
  },
  isActive: {
    type: Boolean,
    default: true
  },
  lastLogin: {
    type: Date
  }
}, {
  timestamps: true
});

Password Hashing

Automatic password hashing on save:
userSchema.pre('save', async function(next) {
  if (!this.isModified('password')) return next();
  
  try {
    const salt = await bcrypt.genSalt(12);
    this.password = await bcrypt.hash(this.password, salt);
    next();
  } catch (error) {
    next(error);
  }
});

Password Comparison

userSchema.methods.comparePassword = async function(candidatePassword) {
  return bcrypt.compare(candidatePassword, this.password);
};

Safe JSON Serialization

Automatic password removal from JSON responses:
toJSON: { 
  transform: function(doc, ret) {
    delete ret.password;
    delete ret.__v;
    return ret;
  }
}

API Endpoints

Register a new user account.Request Body:
{
  "name": "Dr. Juan Pérez",
  "email": "[email protected]",
  "password": "securePassword123"
}
Response:
{
  "success": true,
  "data": {
    "user": {
      "_id": "65f1a2b3c4d5e6f7a8b9c0d1",
      "name": "Dr. Juan Pérez",
      "email": "[email protected]",
      "role": "user",
      "isActive": true
    },
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
  }
}
Authenticate user and receive JWT token.Request Body:
{
  "email": "[email protected]",
  "password": "securePassword123"
}
Response:
{
  "success": true,
  "data": {
    "user": {
      "_id": "65f1a2b3c4d5e6f7a8b9c0d1",
      "name": "Dr. Juan Pérez",
      "email": "[email protected]",
      "role": "user",
      "lastLogin": "2024-03-15T10:30:00.000Z"
    },
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
  }
}

Security Best Practices

Token Storage

Store JWT tokens securely in httpOnly cookies or secure storage. Never expose tokens in URLs.

HTTPS Only

Always use HTTPS in production to prevent token interception.

Token Rotation

Implement token refresh mechanism for long-lived sessions.

Rate Limiting

Login endpoints are protected by rate limiting (100 requests per 15 minutes).

Next Steps

Architecture

Learn about the overall system architecture

API Reference

Explore the complete authentication API

Build docs developers (and LLMs) love