Overview
Allows an authenticated user to change their password by providing their current password and a new password. This endpoint requires valid authentication and verifies the current password before applying the change.
Security Features
Current Password Verification : Validates that the user knows their current password before allowing change
Password Hashing : New password is hashed using bcrypt with salt rounds of 10
Authentication Required : Only the authenticated user can change their own password
Automatic Flag Reset : Clears the must_change_password flag if it was set
Authentication
This endpoint requires authentication via JWT token.
Include the JWT token in the request using one of these methods:
Authorization Header (recommended for API clients):
Authorization: Bearer <token>
HTTP-only Cookie (automatic for browser-based clients):
Request
Body Parameters
The user’s current password for verification. Note : No minimum length is enforced on this field as it’s the existing password. However, validation occurs against the stored hash.
The new password to set for the user account. Recommendation : While no explicit validation is enforced in the change-password endpoint, it’s recommended to use strong passwords with:
Minimum 8-12 characters
Mix of uppercase and lowercase letters
At least one number
At least one special character
Request Example
curl -X POST https://api.example.com/api/auth/change-password \
-H "Content-Type: application/json" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
-d '{
"currentPassword": "oldPassword123",
"newPassword": "newSecurePassword456!"
}'
Response
Success Response (200 OK)
Always true for successful responses.
Contains the operation result. Success message confirming the password change. Value: "Contraseña actualizada correctamente"
Always null for successful responses.
Example Response
{
"success" : true ,
"data" : {
"message" : "Contraseña actualizada correctamente"
},
"error" : null
}
After a successful password change, the user’s current JWT token remains valid. The user does NOT need to log in again.
Error Responses
401 Unauthorized - Missing or Invalid Token
Returned when no authentication token is provided or the token is invalid/expired.
{
"success" : false ,
"data" : null ,
"error" : "No estás autenticado. Por favor, inicia sesión."
}
or
{
"success" : false ,
"data" : null ,
"error" : "Token inválido o expirado."
}
401 Unauthorized - Incorrect Current Password
Returned when the provided currentPassword does not match the user’s actual current password.
{
"success" : false ,
"data" : null ,
"error" : "La contraseña actual es incorrecta"
}
401 Unauthorized - User Not Found
Returned when the authenticated user ID cannot be found in the database. This is a rare edge case that may occur if the user was deleted while their token was still valid.
{
"success" : false ,
"data" : null ,
"error" : "Usuario no encontrado"
}
400 Bad Request - Missing Required Fields
Returned when required fields are missing from the request body.
{
"success" : false ,
"data" : null ,
"error" : "currentPassword and newPassword are required"
}
403 Forbidden - Account Disabled
Returned when the user’s account has been disabled or blocked during the request.
{
"success" : false ,
"data" : null ,
"error" : "Su cuenta ha sido desactivada o bloqueada. Acceso denegado."
}
The authentication middleware checks the user’s account status on every request, ensuring that disabled accounts cannot change their password even with a valid token.
Implementation Details
Source Code References
Route : /workspace/source/backend/domains/auth/auth.routes.js:22
Controller : /workspace/source/backend/domains/auth/auth.controller.js:32
Service : /workspace/source/backend/domains/auth/auth.service.js:97
Authentication Middleware : /workspace/source/backend/middlewares/auth.middleware.js:8
Password Change Flow
Authentication Check : Middleware verifies JWT token and checks account status
User Extraction : User ID is extracted from req.user.usuario_id (set by auth middleware)
User Lookup : Service retrieves user record from database using usuario_id
Current Password Verification : Uses bcrypt.compare() to verify currentPassword against stored password_hash
Password Hashing : New password is hashed using bcrypt.hash() with 10 salt rounds
Database Update : New password hash is stored in the usuarios table
Success Response : Returns confirmation message
Password Hashing
Passwords are hashed using the bcrypt algorithm with the following parameters:
Algorithm : bcrypt
Salt Rounds : 10
Example Hash Format : $2b$10$AbCdEf... (60 characters)
// From auth.service.js:104
const hashedNewPassword = await bcrypt . hash ( newPassword , 10 );
await this . authRepository . updatePassword ( usuarioId , hashedNewPassword );
Security Considerations
Best Practices :
Always verify the current password before changing
Use HTTPS in production to protect passwords in transit
Implement password complexity requirements on the frontend
Consider adding password history to prevent reuse
Log password changes for audit purposes
Usage Examples
JavaScript (Fetch API)
const changePassword = async ( currentPassword , newPassword , token ) => {
try {
const response = await fetch ( 'https://api.example.com/api/auth/change-password' , {
method: 'POST' ,
headers: {
'Content-Type' : 'application/json' ,
'Authorization' : `Bearer ${ token } ` ,
},
body: JSON . stringify ({
currentPassword ,
newPassword ,
}),
});
const data = await response . json ();
if ( ! data . success ) {
throw new Error ( data . error );
}
return data . data ;
} catch ( error ) {
console . error ( 'Password change failed:' , error . message );
throw error ;
}
};
// Usage
const token = localStorage . getItem ( 'token' );
changePassword ( 'oldPassword123' , 'newSecurePassword456!' , token )
. then (( result ) => {
console . log ( 'Success:' , result . message );
alert ( 'Password changed successfully!' );
})
. catch (( error ) => {
if ( error . message . includes ( 'incorrecta' )) {
alert ( 'Current password is incorrect' );
} else {
alert ( 'Failed to change password: ' + error . message );
}
});
JavaScript with Cookie Authentication
// When using cookie-based authentication (browser)
const changePassword = async ( currentPassword , newPassword ) => {
try {
const response = await fetch ( 'https://api.example.com/api/auth/change-password' , {
method: 'POST' ,
headers: {
'Content-Type' : 'application/json' ,
},
credentials: 'include' , // Include HTTP-only cookie
body: JSON . stringify ({
currentPassword ,
newPassword ,
}),
});
const data = await response . json ();
if ( ! data . success ) {
throw new Error ( data . error );
}
return data . data ;
} catch ( error ) {
console . error ( 'Password change failed:' , error . message );
throw error ;
}
};
Python (requests)
import requests
def change_password ( current_password , new_password , token ):
url = 'https://api.example.com/api/auth/change-password'
headers = {
'Content-Type' : 'application/json' ,
'Authorization' : f 'Bearer { token } '
}
payload = {
'currentPassword' : current_password,
'newPassword' : new_password
}
response = requests.post(url, json = payload, headers = headers)
data = response.json()
if not data[ 'success' ]:
raise Exception (data[ 'error' ])
return data[ 'data' ]
# Usage
try :
token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
result = change_password(
'oldPassword123' ,
'newSecurePassword456!' ,
token
)
print (result[ 'message' ])
except Exception as e:
print ( f "Password change failed: { e } " )
React Component Example
import React , { useState } from 'react' ;
function ChangePasswordForm () {
const [ currentPassword , setCurrentPassword ] = useState ( '' );
const [ newPassword , setNewPassword ] = useState ( '' );
const [ confirmPassword , setConfirmPassword ] = useState ( '' );
const [ error , setError ] = useState ( '' );
const [ success , setSuccess ] = useState ( false );
const [ loading , setLoading ] = useState ( false );
const handleSubmit = async ( e ) => {
e . preventDefault ();
setError ( '' );
setSuccess ( false );
// Frontend validation
if ( newPassword !== confirmPassword ) {
setError ( 'New passwords do not match' );
return ;
}
if ( newPassword . length < 8 ) {
setError ( 'New password must be at least 8 characters' );
return ;
}
setLoading ( true );
try {
const token = localStorage . getItem ( 'token' );
const response = await fetch ( '/api/auth/change-password' , {
method: 'POST' ,
headers: {
'Content-Type' : 'application/json' ,
'Authorization' : `Bearer ${ token } ` ,
},
body: JSON . stringify ({
currentPassword ,
newPassword ,
}),
});
const data = await response . json ();
if ( ! data . success ) {
throw new Error ( data . error );
}
setSuccess ( true );
setCurrentPassword ( '' );
setNewPassword ( '' );
setConfirmPassword ( '' );
} catch ( err ) {
setError ( err . message );
} finally {
setLoading ( false );
}
};
return (
< form onSubmit = { handleSubmit } >
< h2 > Change Password </ h2 >
{ error && < div className = "error" > { error } </ div > }
{ success && < div className = "success" > Password changed successfully! </ div > }
< div >
< label > Current Password: </ label >
< input
type = "password"
value = { currentPassword }
onChange = { ( e ) => setCurrentPassword ( e . target . value ) }
required
disabled = { loading }
/>
</ div >
< div >
< label > New Password: </ label >
< input
type = "password"
value = { newPassword }
onChange = { ( e ) => setNewPassword ( e . target . value ) }
required
disabled = { loading }
minLength = { 8 }
/>
</ div >
< div >
< label > Confirm New Password: </ label >
< input
type = "password"
value = { confirmPassword }
onChange = { ( e ) => setConfirmPassword ( e . target . value ) }
required
disabled = { loading }
minLength = { 8 }
/>
</ div >
< button type = "submit" disabled = { loading } >
{ loading ? 'Changing...' : 'Change Password' }
</ button >
</ form >
);
}
export default ChangePasswordForm ;
Forced Password Changes : If a user has must_change_password: true in their login response, implement logic to redirect them to a password change form before allowing access to other features.