Smart Systems for Smarter Operations.
Manage production, inventory, deliveries
and reports in one place.
Dairy
Coffee
Rice
Bananas
Reports
Welcome back
Sign in to continue
Create Your Password
Password setup required.
For security reasons, you must create
a new password before continuing.
```
### Important: one change to the last part
There is one thing I would **not** keep from the code above if Bootstrap's modal needs to close programmatically after a successful save.
This section:
```javascript
modalElement.addEventListener(
"hide.bs.modal",
function (event) {
event.preventDefault();
}
);
```
will prevent **even your successful `modal.hide()`** from closing the modal.
So instead, **remove that entire `hide.bs.modal` listener**. The Bootstrap options already make the modal non-dismissible:
```javascript
backdrop: "static",
keyboard: false
```
Therefore your final `DOMContentLoaded` section should only be:
```javascript
document.addEventListener(
"DOMContentLoaded",
function () {
$("#confirmPassword").on(
"keypress",
function (e) {
if (e.which === 13) {
e.preventDefault();
saveNewPassword();
}
}
);
}
);
```
### Also make sure your controller returns this
Your `Login` action must return:
```csharp
if (string.IsNullOrWhiteSpace(user.PasswordHash))
{
return Json(new
{
success = true,
requirePasswordChange = true,
userid = user.Userid,
message = "You must create a new password before continuing."
});
}
```
And **not** redirect at that point.
Then your `SaveNewPassword` in `HomeController` should be:
```csharp
[HttpPost]
[ValidateAntiForgeryToken]
public async Task SaveNewPassword(
[FromBody] ChangePasswordVm model)
{
try
{
if (model == null)
{
return Json(new
{
success = false,
message = "Invalid request."
});
}
if (string.IsNullOrWhiteSpace(model.NewPassword))
{
return Json(new
{
success = false,
message = "Please enter a new password."
});
}
if (model.NewPassword.Length < 6)
{
return Json(new
{
success = false,
message = "Password must be at least 6 characters."
});
}
if (model.NewPassword != model.ConfirmPassword)
{
return Json(new
{
success = false,
message = "Passwords do not match."
});
}
var user = await _context.UserAccounts
.FirstOrDefaultAsync(x =>
x.Userid == model.Userid);
if (user == null)
{
return Json(new
{
success = false,
message = "User account not found."
});
}
if (!user.Active)
{
return Json(new
{
success = false,
message = "User account is inactive."
});
}
/*
* This is specifically for accounts
* that don't have a password yet.
*/
if (!string.IsNullOrWhiteSpace(user.PasswordHash))
{
return Json(new
{
success = false,
message = "This account already has a password."
});
}
/*
* HASH THE PASSWORD
*/
var passwordHasher =
new PasswordHasher();
user.PasswordHash =
passwordHasher.HashPassword(
user,
model.NewPassword
);
await _context.SaveChangesAsync();
/*
* Find company
*/
var company =
await _context.DCompanies
.FirstOrDefaultAsync(x =>
x.Name.ToUpper() ==
user.Branchcode.ToUpper());
/*
* Create session if OTP is not required
*/
if (company != null &&
!company.ToReceiveOTP)
{
_sessionService.SetUserSession(
user,
company);
}
return Json(new
{
success = true,
message =
"Password created successfully.",
redirectUrl =
Url.Action(
"Index2",
"Home")
});
}
catch (Exception ex)
{
return Json(new
{
success = false,
message = ex.Message
});
}
}
```
And your ViewModel should be:
```csharp
public class ChangePasswordVm
{
public long Userid { get; set; }
public string NewPassword { get; set; }
public string ConfirmPassword { get; set; }
}
```
### One final important point
Your current login page still has:
```cshtml
asp-for="Password"
```
and sends:
```javascript
Password: password
```
That is **fine for the login request**. It does **not** mean you need a `Password` column in `UserAccount`. The `Password` here is simply the password the user typed into the login form. Your database should only contain:
```csharp
public string? PasswordHash { get; set; }
```
The flow will therefore be:
**Existing user with hash:**
```text
Username + Password
↓
VerifyHashedPassword()
↓
Login
```
**User with `PasswordHash = NULL`:**
```text
Username + Password
↓
PasswordHash is NULL
↓
Open Create Your Password popup
↓
New Password + Confirm Password
↓
HashPassword()
↓
Save PasswordHash
↓
Continue to system
```
This specifically avoids using `Reset` for the migration, as you requested.