Overview
AmbioSys implements a JWT (JSON Web Token) based authentication system that provides secure user authentication, session management, and comprehensive access logging. The system handles user registration, login, profile management, and token-based authorization.
Authentication Flow
User Registration
Users are registered through the /api/register endpoint with required personal information, department assignment, and optional license data.
JWT Token Generation
Upon successful registration or login, a JWT token is generated with user data and an 8-hour expiration time.
Token-Based Requests
Authenticated requests include the JWT token in the Authorization header as Bearer <token>.
Token Verification
The authentication middleware validates the token on protected routes and extracts user information.
User Registration
The registration endpoint creates a new user account with comprehensive profile information.
Endpoint
Request Body
{
"usuarioLogin": "jdoe",
"usuarioCorreo": "[email protected]",
"usuarioPassword": "securepassword123",
"usuarioNombre": "John",
"usuarioApellido": "Doe",
"departamentoId": 1,
"usuarioFechaNacimiento": "1990-01-15",
"usuarioCelular": "+502 1234-5678",
"usuarioDpi": "1234567890101",
"usuarioTipoDocumento": 1,
"usuarioNit": "12345678",
"poseeLicencia": true,
"licenciaTipo": 2,
"licenciaNumero": "A-12345",
"licenciaPrimerAnio": 2015,
"licenciaFechaVencimiento": "2025-12-31",
"usuarioRolId": 3
}
Validation Rules
usuarioLogin: Required, non-empty username
usuarioCorreo: Required, valid email format
usuarioPassword: Required, minimum 6 characters
usuarioNombre: Required, non-empty first name
usuarioApellido: Required, non-empty last name
departamentoId: Required, valid department ID (integer)
- License fields: Required only if
poseeLicencia is true
License information is mandatory when poseeLicencia is set to true. The system validates that licenciaTipo and licenciaNumero are provided and valid.
Response
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"usuario_id": 42,
"usuario_login": "jdoe",
"usuario_correo": "[email protected]",
"usuario_nombre": "John",
"usuario_apellido": "Doe",
"departamento_id": 1,
"usuario_celular": "+502 1234-5678",
"profile": null
}
}
Implementation Details
From /Backend/web-ambiotec/src/routes/auth.js:47-173:
router.post('/register',
[
check('usuarioLogin').notEmpty(),
check('usuarioCorreo').isEmail(),
check('usuarioPassword').isLength({ min: 6 }),
check('usuarioNombre').notEmpty(),
check('usuarioApellido').notEmpty(),
check('departamentoId').isInt(),
],
async (req, res) => {
// Validation
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({
error: 'Datos inválidos',
details: errors.array()
});
}
// License validation logic
const hasLicense = !!poseeLicencia;
if (hasLicense) {
if (!Number.isInteger(licenseTypeId) || licenseTypeId <= 0) {
return res.status(400).json({
error: 'Tipo de licencia invalido'
});
}
}
// Call database function
const { rows } = await pgPool.query(
`SELECT * FROM db_ambiotec.fn_register_user($1, $2, ..., $16)`,
datasend
);
// Generate JWT token
const token = jwt.sign(
userData,
JWT_SECRET,
{ expiresIn: JWT_EXPIRES }
);
return res.status(201).json({ token, user: userData });
}
);
User Login
The login endpoint authenticates users and logs access attempts.
Endpoint
Request Body
{
"usuario_login": "jdoe",
"usuario_password": "securepassword123"
}
Response
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"usuario_id": 42,
"usuario_login": "jdoe",
"usuario_correo": "[email protected]",
"usuario_nombre": "John",
"usuario_apellido": "Doe",
"departamento_id": 1,
"usuario_celular": "+502 1234-5678",
"access_id": 1234,
"profile": null
}
}
Access Logging
Every login attempt is logged with client metadata:
const client = parseClient(req);
const access_id = await logAccess({
user_id: user.user_id,
...client, // ip, platform, browser, etc.
is_successful: true,
extra_data: req.headers['x-client-info'] || null
});
The access_id returned in the login response can be used to track the user’s session throughout the application.
Error Handling
From /Backend/web-ambiotec/src/routes/auth.js:255-279:
catch (err) {
// Log failed access attempt
await logAccess({
user_id: null,
...client,
is_successful: false,
event: 'login'
});
// Database connection errors
if (err.code === 'ECONNREFUSED' || err.code === 'ETIMEDOUT') {
return res.status(503).json({
error: 'Servicio no disponible',
message: 'No se puede conectar con la base de datos.'
});
}
// Invalid credentials (P0002 from database function)
if (err.code === 'P0002') {
return res.status(401).json({ error: err.message });
}
return res.status(500).json({
error: 'Error interno al iniciar sesión'
});
}
JWT Token Structure
Token Configuration
From /Backend/web-ambiotec/src/routes/auth.js:12-13:
const JWT_SECRET = constants.APP.JWT_SECRET || '...';
const JWT_EXPIRES = constants.APP.JWT_EXPIRES || '8h';
Token Payload
The JWT token contains the following user data:
{
"usuario_id": 42,
"usuario_login": "jdoe",
"usuario_correo": "[email protected]",
"usuario_nombre": "John",
"usuario_apellido": "Doe",
"departamento_id": 1,
"usuario_celular": "+502 1234-5678",
"access_id": 1234,
"profile": null,
"iat": 1234567890,
"exp": 1234596690
}
Store JWT tokens securely on the client side. Never expose tokens in URLs or logs. Use secure storage mechanisms like httpOnly cookies or secure local storage.
Authentication Middleware
The requireAuth middleware protects routes that require authentication.
Implementation
From /Backend/web-ambiotec/src/middleware/auth.js:7-20:
export function requireAuth(req, res, next) {
const auth = req.headers.authorization;
// Check for Bearer token
if (!auth?.startsWith('Bearer ')) {
return res.status(401).json({ error: 'Token no provisto' });
}
const token = auth.split(' ')[1];
try {
// Verify and decode token
const payload = jwt.verify(token, JWT_SECRET);
req.user = payload; // Attach user data to request
next();
} catch {
return res.status(401).json({ error: 'Token inválido o caducado' });
}
}
Usage Example
import { requireAuth } from '../middleware/auth.js';
router.get('/protected-route', requireAuth, async (req, res) => {
const userId = req.user.usuario_id;
// Access user data from req.user
});
Making Authenticated Requests
curl -X GET https://api.ambiosys.com/api/protected-route \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
Profile Management
Get User Profile
Retrieve complete user profile information including roles and license data.
Update User Profile
Update user profile information with role assignments.
From /Backend/web-ambiotec/src/routes/auth.js:315-403:
router.post('/update-profile',
[
check('user_id').isInt(),
check('username').notEmpty(),
check('email').isEmail(),
check('first_name').notEmpty(),
check('last_name').notEmpty(),
check('mobile_number').notEmpty(),
check('department_id').isInt(),
check('roles').optional().isArray(),
],
async (req, res) => {
await client.query('BEGIN');
// Update user profile
const { rows } = await client.query(
`SELECT * FROM db_ambiotec.fn_update_user_profile(...)`
);
// Update role assignments
if (Array.isArray(roles)) {
await client.query(
'DELETE FROM db_ambiotec.user_roles WHERE user_id = $1',
[user_id]
);
for (const role of roles) {
await client.query(
`INSERT INTO db_ambiotec.user_roles (user_id, role_id)
VALUES ($1, $2) ON CONFLICT DO NOTHING`,
[user_id, roleId]
);
}
}
await client.query('COMMIT');
}
);
Profile updates are transactional. If any part of the update fails, all changes are rolled back to maintain data consistency.
Session Management
Token Expiration
- Default expiration: 8 hours
- Configured via
JWT_EXPIRES constant
- No automatic token refresh (implement token refresh endpoint if needed)
Access Tracking
Each login generates an access_id that can be used for:
- Session tracking
- Audit logging
- Activity monitoring
- Security analysis
Security Best Practices
Use HTTPS Only
Always transmit JWT tokens over secure HTTPS connections to prevent token interception.
Implement Token Refresh
Consider implementing a token refresh mechanism for long-lived sessions without requiring re-authentication.
Validate on Every Request
The middleware validates tokens on every protected request, ensuring expired or invalid tokens are rejected.
Monitor Failed Attempts
All failed login attempts are logged with client metadata for security monitoring and anomaly detection.
Department and License Types
Get Departments
Retrieve available departments for user assignment.
Response:
{
"success": true,
"departamentos": [
{
"department_id": 1,
"department_name": "Operaciones",
"description": "Personal operativo de campo"
}
]
}
Get License Types
Retrieve available license types for driver licensing.
Response:
{
"success": true,
"licenseTypes": [
{
"license_type_id": 2,
"license_type_name": "Tipo A",
"license_type_description": "Vehículos livianos"
}
]
}
License information triggers automated reminder scheduling for license renewal notifications when expiration dates are set.
Error Responses
400 Bad Request
{
"error": "Datos inválidos",
"details": [
{
"msg": "Invalid value",
"param": "usuarioCorreo",
"location": "body"
}
]
}
401 Unauthorized
{
"error": "Token inválido o caducado"
}
409 Conflict
{
"error": "Login o correo ya registrado"
}
503 Service Unavailable
{
"error": "Servicio no disponible",
"message": "No se puede conectar con la base de datos. Por favor, contacte a soporte del sistema."
}