Skip to main content
POST
/
api
/
auth
/
change-password
Change Password
curl --request POST \
  --url https://api.example.com/api/auth/change-password \
  --header 'Content-Type: application/json' \
  --data '
{
  "currentPassword": "<string>",
  "newPassword": "<string>"
}
'
{
  "success": true,
  "data": {
    "message": "<string>"
  },
  "error": null
}

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:
  1. Authorization Header (recommended for API clients):
    Authorization: Bearer <token>
    
  2. HTTP-only Cookie (automatic for browser-based clients):
    Cookie: token=<token>
    

Request

Body Parameters

currentPassword
string
required
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.
newPassword
string
required
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)

success
boolean
Always true for successful responses.
data
object
Contains the operation result.
error
null
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

  1. Authentication Check: Middleware verifies JWT token and checks account status
  2. User Extraction: User ID is extracted from req.user.usuario_id (set by auth middleware)
  3. User Lookup: Service retrieves user record from database using usuario_id
  4. Current Password Verification: Uses bcrypt.compare() to verify currentPassword against stored password_hash
  5. Password Hashing: New password is hashed using bcrypt.hash() with 10 salt rounds
  6. Database Update: New password hash is stored in the usuarios table
  7. 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:
  1. Always verify the current password before changing
  2. Use HTTPS in production to protect passwords in transit
  3. Implement password complexity requirements on the frontend
  4. Consider adding password history to prevent reuse
  5. 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);
    }
  });
// 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.

Build docs developers (and LLMs) love