Fixed login form, navigation
This commit is contained in:
parent
27aaee6293
commit
51766eca42
4 changed files with 175 additions and 182 deletions
|
@ -4,16 +4,16 @@
|
||||||
ViewData["Title"] = "Authenticate";
|
ViewData["Title"] = "Authenticate";
|
||||||
}
|
}
|
||||||
|
|
||||||
<div class="row" style="display: flex; flex-direction: row;">
|
<div class="row">
|
||||||
<div class="col-md-4">
|
<div class="col-md-6 offset-md-3">
|
||||||
<form method="post">
|
<form method="post">
|
||||||
<h4>Login</h4>
|
<h4>Login or Register</h4>
|
||||||
<hr />
|
<hr />
|
||||||
@if (Model.LoginErrorMessages != null && Model.LoginErrorMessages.Any())
|
@if (Model.ErrorMessages != null && Model.ErrorMessages.Any())
|
||||||
{
|
{
|
||||||
<div asp-validation-summary="ModelOnly" class="text-danger" role="alert">
|
<div class="text-danger" role="alert">
|
||||||
<ul>
|
<ul>
|
||||||
@foreach (var error in Model.LoginErrorMessages)
|
@foreach (var error in Model.ErrorMessages)
|
||||||
{
|
{
|
||||||
<li>@error</li>
|
<li>@error</li>
|
||||||
}
|
}
|
||||||
|
@ -21,52 +21,23 @@
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
<div class="form-floating mb-3">
|
<div class="form-floating mb-3">
|
||||||
<input asp-for="LoginInput.Username" class="form-control" autocomplete="username" aria-required="true" placeholder="username" />
|
<input asp-for="Input.Username" class="form-control" autocomplete="username" aria-required="true" placeholder="username" />
|
||||||
<label asp-for="LoginInput.Username">Username</label>
|
<label asp-for="Input.Username">Username</label>
|
||||||
<span asp-validation-for="LoginInput.Username" class="text-danger"></span>
|
<span asp-validation-for="Input.Username" class="text-danger"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-floating mb-3">
|
<div class="form-floating mb-3">
|
||||||
<input asp-for="LoginInput.Password" class="form-control" type="password" autocomplete="current-password" aria-required="true" placeholder="password" />
|
<input asp-for="Input.Password" class="form-control" type="password" autocomplete="current-password" aria-required="true" placeholder="password" />
|
||||||
<label asp-for="LoginInput.Password">Password</label>
|
<label asp-for="Input.Password">Password</label>
|
||||||
<span asp-validation-for="LoginInput.Password" class="text-danger"></span>
|
<span asp-validation-for="Input.Password" class="text-danger"></span>
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<button type="submit" asp-page-handler="Login" class="w-100 btn btn-lg btn-secondary">Login</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-4">
|
|
||||||
<form method="post">
|
|
||||||
<h4>Register</h4>
|
|
||||||
<hr />
|
|
||||||
@if (Model.RegisterErrorMessages != null && Model.RegisterErrorMessages.Any())
|
|
||||||
{
|
|
||||||
<div asp-validation-summary="ModelOnly" class="text-danger" role="alert">
|
|
||||||
<ul>
|
|
||||||
@foreach (var error in Model.RegisterErrorMessages)
|
|
||||||
{
|
|
||||||
<li>@error</li>
|
|
||||||
}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
<div class="form-floating mb-3">
|
|
||||||
<input asp-for="RegisterInput.Username" class="form-control" autocomplete="username" aria-required="true" placeholder="username" />
|
|
||||||
<label asp-for="RegisterInput.Username">Username</label>
|
|
||||||
<span asp-validation-for="RegisterInput.Username" class="text-danger"></span>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="form-floating mb-3">
|
<div class="form-floating mb-3">
|
||||||
<input asp-for="RegisterInput.Email" class="form-control" autocomplete="email" placeholder="name@example.com" />
|
<input asp-for="Input.Email" class="form-control" autocomplete="email" placeholder="name@example.com" id="Input_Email" />
|
||||||
<label asp-for="RegisterInput.Email">Email</label>
|
<label asp-for="Input.Email">Email (Optional for Login)</label>
|
||||||
<span asp-validation-for="RegisterInput.Email" class="text-danger"></span>
|
<span asp-validation-for="Input.Email" class="text-danger"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-floating mb-3">
|
<div class="d-grid gap-2 d-md-flex justify-content-md-between">
|
||||||
<input asp-for="RegisterInput.Password" class="form-control" type="password" autocomplete="current-password" aria-required="true" placeholder="password" />
|
<button type="submit" asp-page-handler="Login" class="btn btn-lg btn-secondary flex-grow-1 me-2" id="loginButton">Login</button>
|
||||||
<label asp-for="RegisterInput.Password">Password</label>
|
<button type="submit" asp-page-handler="Register" class="btn btn-lg btn-primary flex-grow-1 ms-2">Register</button>
|
||||||
<span asp-validation-for="RegisterInput.Password" class="text-danger"></span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<button type="submit" asp-page-handler="Register" class="w-100 btn btn-lg btn-primary">Login</button>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -76,13 +47,19 @@
|
||||||
<partial name="_ValidationScriptsPartial" />
|
<partial name="_ValidationScriptsPartial" />
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
// when login button is clicked, remove validation from email field
|
// When the login button is clicked
|
||||||
$("button[asp-page-handler='Login']").click(function() {
|
$("#loginButton").click(function() {
|
||||||
$("#Input_Email").removeAttr("data-val");
|
var emailInput = $("#Input_Email");
|
||||||
$("#Input_Email").removeAttr("data-val-required");
|
var form = $("#authForm");
|
||||||
// revalidate the form to clear error
|
|
||||||
$("form").validate().element("#Input_Email");
|
// Remove the data-val-required attribute for client-side validation
|
||||||
return true;
|
emailInput.removeAttr("data-val-required");
|
||||||
|
|
||||||
|
// If jQuery Validate has already initialized the form,
|
||||||
|
// re-parse the validation for the email field to reflect the change.
|
||||||
|
if (form.data('validator')) {
|
||||||
|
form.validate().element(emailInput);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -7,56 +7,32 @@ using System.Threading.Tasks;
|
||||||
using CSR.Application.Interfaces;
|
using CSR.Application.Interfaces;
|
||||||
using CSR.Domain.Entities;
|
using CSR.Domain.Entities;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using Microsoft.AspNetCore.Authentication;
|
using Microsoft.AspNetCore.Authentication;
|
||||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
// If you are using ASP.NET Core Identity for sign-in management, you might need:
|
|
||||||
// using Microsoft.AspNetCore.Identity;
|
|
||||||
// using System.Security.Claims;
|
|
||||||
|
|
||||||
public class AuthModel(IUserService userService) : PageModel
|
public class AuthModel(IUserService userService) : PageModel
|
||||||
{
|
{
|
||||||
private readonly IUserService _userService = userService;
|
private readonly IUserService _userService = userService;
|
||||||
|
|
||||||
[BindProperty]
|
[BindProperty]
|
||||||
public LoginModel LoginInput { get; set; } = new();
|
public InputModel Input { get; set; } = new();
|
||||||
|
|
||||||
[BindProperty]
|
public List<string> ErrorMessages { get; set; } = [];
|
||||||
public RegisterModel RegisterInput { get; set; } = new();
|
|
||||||
|
|
||||||
public List<string> LoginErrorMessages { get; set; } = [];
|
public class InputModel
|
||||||
public List<string> RegisterErrorMessages { get; set; } = [];
|
|
||||||
|
|
||||||
public class LoginModel
|
|
||||||
{
|
|
||||||
[Required(ErrorMessage = "Username is required.")]
|
|
||||||
[Display(Name = "Username")]
|
|
||||||
public string Username { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
[Required]
|
|
||||||
[DataType(DataType.Password)]
|
|
||||||
[Display(Name = "Password")]
|
|
||||||
public string Password { get; set; } = string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class RegisterModel
|
|
||||||
{
|
{
|
||||||
[Required(ErrorMessage = "Username is required.")]
|
[Required(ErrorMessage = "Username is required.")]
|
||||||
[Display(Name = "Username")]
|
[Display(Name = "Username")]
|
||||||
[StringLength(32, ErrorMessage = "Username cannot be longer than 32 characters.")]
|
[StringLength(32, ErrorMessage = "Username cannot be longer than 32 characters.")]
|
||||||
[RegularExpression(@"^[a-zA-Z0-9_]+$", ErrorMessage = "Username can only contain letters, numbers and underscore.")]
|
[RegularExpression(@"^[a-zA-Z0-9_]+$", ErrorMessage = "Username can only contain letters, numbers and underscore.")]
|
||||||
[DataType(DataType.Text)]
|
|
||||||
public string Username { get; set; } = string.Empty;
|
public string Username { get; set; } = string.Empty;
|
||||||
|
|
||||||
[Required(ErrorMessage = "Email is required for registration.")]
|
|
||||||
[EmailAddress]
|
[EmailAddress]
|
||||||
[Display(Name = "Email")]
|
[Display(Name = "Email")]
|
||||||
public string Email { get; set; } = string.Empty;
|
public string? Email { get; set; } = string.Empty;
|
||||||
|
|
||||||
[Required]
|
[Required(ErrorMessage = "Password is required.")]
|
||||||
[DataType(DataType.Password)]
|
[DataType(DataType.Password)]
|
||||||
[Display(Name = "Password")]
|
[Display(Name = "Password")]
|
||||||
public string Password { get; set; } = string.Empty;
|
public string Password { get; set; } = string.Empty;
|
||||||
|
@ -64,62 +40,32 @@ public class AuthModel(IUserService userService) : PageModel
|
||||||
|
|
||||||
public void OnGet()
|
public void OnGet()
|
||||||
{
|
{
|
||||||
}
|
ErrorMessages.Clear();
|
||||||
|
|
||||||
public async Task<IActionResult> OnPostRegisterAsync()
|
|
||||||
{
|
|
||||||
if (!ModelState.IsValid)
|
|
||||||
{
|
|
||||||
RegisterErrorMessages.Add("Please correct the form errors.");
|
|
||||||
return Page();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Email is required for registration
|
|
||||||
if (string.IsNullOrWhiteSpace(RegisterInput.Email))
|
|
||||||
{
|
|
||||||
RegisterErrorMessages.Add("Email is required for registration.");
|
|
||||||
return Page();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var result = await _userService.RegisterNewUser(RegisterInput.Username, RegisterInput.Email, RegisterInput.Password);
|
|
||||||
|
|
||||||
if (result.User != null)
|
|
||||||
{
|
|
||||||
// Registration successful
|
|
||||||
await _userService.Login(RegisterInput.Username, RegisterInput.Password);
|
|
||||||
|
|
||||||
await SendCookies(result.User.Username, result.User.Role.Name);
|
|
||||||
|
|
||||||
return RedirectToPageBasedOnRole(result.User);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (result.Errors != null)
|
|
||||||
{
|
|
||||||
RegisterErrorMessages.AddRange(result.Errors);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
RegisterErrorMessages.Add("An unknown error occurred during registration.");
|
|
||||||
}
|
|
||||||
return Page();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IActionResult> OnPostLoginAsync()
|
public async Task<IActionResult> OnPostLoginAsync()
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(LoginInput.Username) || string.IsNullOrWhiteSpace(LoginInput.Password))
|
// Clear any previous registration errors
|
||||||
|
ModelState.Remove("Input.Email");
|
||||||
|
|
||||||
|
if (!ModelState.IsValid)
|
||||||
{
|
{
|
||||||
LoginErrorMessages.Add("Username and Password are required.");
|
foreach (var modelState in ModelState.Values)
|
||||||
|
{
|
||||||
|
foreach (var e in modelState.Errors)
|
||||||
|
{
|
||||||
|
ErrorMessages.Add(e.ErrorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
return Page();
|
return Page();
|
||||||
}
|
}
|
||||||
|
|
||||||
var (user, error) = await _userService.Login(LoginInput.Username, LoginInput.Password);
|
var (user, error) = await _userService.Login(Input.Username, Input.Password);
|
||||||
|
|
||||||
if (user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
LoginErrorMessages.Add(error ?? "Login failed.");
|
ErrorMessages.Add(error ?? "Login failed.");
|
||||||
|
Console.WriteLine(error);
|
||||||
return Page();
|
return Page();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,6 +74,54 @@ public class AuthModel(IUserService userService) : PageModel
|
||||||
return RedirectToPageBasedOnRole(user);
|
return RedirectToPageBasedOnRole(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<IActionResult> OnPostLogoutAsync()
|
||||||
|
{
|
||||||
|
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
|
||||||
|
return RedirectToPage("/Index"); // Redirect to home page after logout
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IActionResult> OnPostRegisterAsync()
|
||||||
|
{
|
||||||
|
// Email is required for registration, so add a validation error if it's missing
|
||||||
|
if (string.IsNullOrWhiteSpace(Input.Email))
|
||||||
|
{
|
||||||
|
ModelState.AddModelError("Input.Email", "Email is required for registration.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ModelState.IsValid)
|
||||||
|
{
|
||||||
|
foreach (var modelState in ModelState.Values)
|
||||||
|
{
|
||||||
|
foreach (var error in modelState.Errors)
|
||||||
|
{
|
||||||
|
ErrorMessages.Add(error.ErrorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Page();
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = await _userService.RegisterNewUser(Input.Username, Input.Email!, Input.Password);
|
||||||
|
|
||||||
|
if (result.User != null)
|
||||||
|
{
|
||||||
|
await _userService.Login(Input.Username, Input.Password);
|
||||||
|
await SendCookies(result.User.Username, result.User.Role.Name);
|
||||||
|
return RedirectToPageBasedOnRole(result.User);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (result.Errors != null)
|
||||||
|
{
|
||||||
|
ErrorMessages.AddRange(result.Errors);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ErrorMessages.Add("An unknown error occurred during registration.");
|
||||||
|
}
|
||||||
|
return Page();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task SendCookies(string username, string role)
|
private async Task SendCookies(string username, string role)
|
||||||
{
|
{
|
||||||
var claims = new List<Claim>
|
var claims = new List<Claim>
|
||||||
|
@ -152,7 +146,6 @@ public class AuthModel(IUserService userService) : PageModel
|
||||||
{
|
{
|
||||||
if (user == null || string.IsNullOrWhiteSpace(user.Role.Name))
|
if (user == null || string.IsNullOrWhiteSpace(user.Role.Name))
|
||||||
{
|
{
|
||||||
// Default redirect if role is not defined or user is null
|
|
||||||
return RedirectToPage("/Index");
|
return RedirectToPage("/Index");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,9 +19,32 @@
|
||||||
</button>
|
</button>
|
||||||
<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
|
<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
|
||||||
<ul class="navbar-nav flex-grow-1">
|
<ul class="navbar-nav flex-grow-1">
|
||||||
|
@if (User.Identity.IsAuthenticated)
|
||||||
|
{
|
||||||
|
if (User.IsInRole("Admin"))
|
||||||
|
{
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link text-dark" asp-area="" asp-page="/Auth">Login/Register</a>
|
<a class="nav-link text-dark" asp-area="" asp-page="/Admin">[Admin Page]</a>
|
||||||
</li>
|
</li>
|
||||||
|
}
|
||||||
|
if (User.IsInRole("User") || User.IsInRole("Admin"))
|
||||||
|
{
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link text-dark" asp-area="" asp-page="/User">[User Profile]</a>
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
<li class="nav-item">
|
||||||
|
<form class="form-inline" asp-page="/Auth" asp-page-handler="Logout" method="post">
|
||||||
|
<button type="submit" class="nav-link btn btn-link text-dark">[Logout]</button>
|
||||||
|
</form>
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link text-dark" asp-area="" asp-page="/Auth">[Login or Register]</a>
|
||||||
|
</li>
|
||||||
|
}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -7,6 +7,6 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Database": {
|
"Database": {
|
||||||
"Path": "/home/danial23/Desktop/csr.db"
|
"Path": "/home/danial23/dl/csr.db"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue