I have an app in which users can track food trucks. On the user dashboard, users can update their location. Upon confirming the location update, the client sends a .put request to the backend. The problem is that the .put request is updating every value for the user account object(i.e. id, username, email, password AND location).
In the input that allows the user to change their location, the only value that is modified is location. However, I think there's a bug that changes the password as well. The password is stored as a hash value. Therefore, when the put request is made, I think the location update onClick is passing in the hashed value of the password for the user, which then changes the password in the database. When I try to login with a user that has just changed their location, I get an error back saying invalid credentials because of this. How can I solve this problem?:
Here is the action in question:
// edit location for diners
export const editLocation = (changes, id) => dispatch => {
dispatch({ type: EDIT_LOCATION_START })
axiosWithAuth()
.put(`https://foodtrucktrackr.herokuapp.com/api/diner/${id}`, changes)
.then(res => {
console.log(res);
dispatch({ type: EDIT_LOCATION_SUCCESS, payload: res.data })
})
}
DinerDash.js (condensed to only the relevant lines):
const DinerDash = props => {
const [locationEditMode, setLocationEditMode] = useState(false);
const [updatedAccount, setUpdatedAccount] = useState({
id: props.id,
username: props.username,
email: props.email,
password: props.password,
location: ''
})
const changeLocation = e => {
console.log(updatedAccount);
e.preventDefault();
props.editLocation(updatedAccount, props.id);
setLocationEditMode(false);
}
const handleLocationChange = e => {
setUpdatedAccount({
...updatedAccount,
location: e.target.value
})
}
return (
<div>
<h1>This is the diner Dashboard component</h1>
<h2>Welcome, {accountInfo.username}</h2>
<p>
Find trucks near: {props.location}
<ArrowDropDownCircleIcon
className="location-edit-icon"
onClick={() => setLocationEditMode(!locationEditMode)}
/>
</p>
{locationEditMode && <div>
<input placeholder="Enter a new location" onChange={handleLocationChange}/>
<button onClick={changeLocation}>Done</button>
</div>}
<br />
<button onClick={logout}>Logout</button>
</div>
)
}
const mapStateToProps = state => {
return {
id: state.account.id,
username: state.account.username,
email: state.account.email,
password: state.account.password,
location: state.account.location
}
}
code from the reducer:
case EDIT_LOCATION_START:
return {
...state,
isLoading: true
}
case EDIT_LOCATION_SUCCESS:
return {
...state,
isLoading: false,
account: {
id: action.payload.id,
username: action.payload.username,
email: action.payload.email,
password: action.payload.password,
location: action.payload.location
}
code from the backend:
// how diners edit account
router.put('/:id', (req, res) => {
const { id } = req.params;
let updatedDiner = req.body;
updatedDiner.id = id;
const hash = bcrypt.hashSync(updatedDiner.password, 8);
updatedDiner.password = hash;
diners.editDiner(updatedDiner, id)
.then(updated => {
res.status(200).json(updated);
})
.catch(err => {
console.log(err);
res.status(500).json({ errorMessage: 'An error occured while updating account' });
})
})
Try to send an empty password , when there is no change in the password, keep a separate variable for password other than the default password key , In backend check if its not empty and then proceed.
Backend :
router.put('/:id', (req, res) => {
const { id } = req.params;
let updatedDiner = req.body;
updatedDiner.id = id;
if (updatedDiner.password){
const hash = bcrypt.hashSync(updatedDiner.password, 8);
updatedDiner.password = hash;
}
else
{
delete updatedDiner.password
}
diners.editDiner(updatedDiner, id)
.then(updated => {
res.status(200).json(updated);
})
.catch(err => {
console.log(err);
res.status(500).json({ errorMessage: 'An error occured while updating account' });
})
})