Overview
The user login endpoint authenticates existing users by validating their email and password credentials. Passwords are securely compared using bcrypt.
Endpoint
Request Body
Field Type Required Description emailstring Yes User’s registered email address passwordstring Yes User’s password (plain text)
Only email and password are required for login. The password is compared against the hashed version stored in the database.
Implementation Details
Password Verification
The system uses bcrypt to securely compare the provided password with the stored hash:
const isPasswordValid = await bcrypt . compare ( password , user . password );
if ( ! isPasswordValid ) {
return res . status ( 401 ). json ({ message: 'Credenciales inválidas' });
}
Never compare passwords using simple string equality. Always use bcrypt.compare() to validate hashed passwords.
Authentication Process
Required Fields Check
Validate that both email and password are provided. if ( ! email || ! password ) {
return res . status ( 400 ). json ({ message: 'Email y contraseña son obligatorios' });
}
User Lookup
Find the user by email address. const user = await User . findOne ({ email });
if ( ! user ) {
return res . status ( 401 ). json ({ message: 'Usuario no encontrado con el email proporcionado' });
}
Password Verification
Use bcrypt to compare the provided password with the stored hash. const isPasswordValid = await bcrypt . compare ( password , user . password );
if ( ! isPasswordValid ) {
return res . status ( 401 ). json ({ message: 'Credenciales inválidas' });
}
Response Generation
Return user information excluding the password. const userInfo = {
document: user . document ,
email: user . email ,
name: user . name ,
last_name: user . last_name ,
cellphone: user . cellphone ,
user_type: user . user_type ,
};
res . status ( 200 ). json ({ message: 'Usuario logueado correctamente' , user: userInfo });
Examples
JavaScript (Fetch)
Python (Requests)
cURL
const response = await fetch ( 'http://localhost:3000/login' , {
method: 'POST' ,
headers: {
'Content-Type' : 'application/json' ,
},
body: JSON . stringify ({
email: '[email protected] ' ,
password: 'SecurePassword123!'
})
});
const data = await response . json ();
console . log ( data );
// Store user information for subsequent requests
if ( response . ok ) {
localStorage . setItem ( 'user' , JSON . stringify ( data . user ));
}
Response
Success Response (200 OK)
{
"message" : "Usuario logueado correctamente" ,
"user" : {
"document" : "1234567890" ,
"email" : "[email protected] " ,
"name" : "John" ,
"last_name" : "Doe" ,
"cellphone" : "+1234567890" ,
"user_type" : "contractor"
}
}
The password field is explicitly excluded from the response for security reasons. Only safe user information is returned.
Error Responses
Missing Required Fields (400 Bad Request)
{
"message" : "Email y contraseña son obligatorios"
}
Cause : Either email or password field is missing from the request body.
User Not Found (401 Unauthorized)
{
"message" : "Usuario no encontrado con el email proporcionado"
}
Cause : No user exists with the provided email address.
Invalid Credentials (401 Unauthorized)
{
"message" : "Credenciales inválidas"
}
Cause : The password doesn’t match the stored hash for the user.
Server Error (500 Internal Server Error)
{
"message" : "Error al iniciar sesión" ,
"error" : {}
}
Cause : An unexpected server error occurred during the login process.
Security Considerations
Important security considerations:
Timing attacks : The current implementation may be vulnerable to timing attacks. Consider using constant-time comparison.
Rate limiting : Implement rate limiting to prevent brute-force attacks.
Account lockout : Consider locking accounts after multiple failed login attempts.
Error messages : The current error messages reveal whether an email exists in the system. Consider using generic messages.
Edge Cases
Case Sensitivity
// Email lookup is case-sensitive
const user = await User . findOne ({ email });
Recommendation : Convert emails to lowercase before storing and comparing:
const normalizedEmail = email . toLowerCase ();
const user = await User . findOne ({ email: normalizedEmail });
Empty Password Fields
While the validation checks for missing fields, empty strings will pass the check:
Recommendation : Add length validation:
if ( ! email || ! password || password . length === 0 ) {
return res . status ( 400 ). json ({ message: 'Email y contraseña son obligatorios' });
}
Database Connection Failures
If the database connection fails, a 500 error is returned without specific details:
res . status ( 500 ). json ({ message: 'Error al iniciar sesión' , error });
Best Practices
Session Management : After successful login, implement JWT tokens or session cookies for subsequent requests
HTTPS Only : Always use HTTPS in production to encrypt credentials in transit
Password Policies : Enforce strong password requirements during registration
Multi-Factor Authentication : Consider adding MFA for enhanced security
Audit Logging : Log login attempts for security monitoring
Token Expiration : Implement automatic logout after period of inactivity
Session Management
The current implementation doesn’t include session management or JWT tokens. After successful login, you’ll need to implement your own session handling mechanism.
Recommended Next Steps
Implement JWT token generation on successful login
Add authentication middleware to protect routes
Create token refresh mechanism
Implement logout functionality