récupération de classes

This commit is contained in:
2026-01-18 15:37:36 +01:00
parent 0f3007f0ea
commit 29c33d015d
9 changed files with 207 additions and 7 deletions

View File

@@ -19,6 +19,7 @@
<ItemGroup> <ItemGroup>
<Folder Include="Migrations\" /> <Folder Include="Migrations\" />
<Folder Include="Services\" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -18,4 +18,9 @@ public class UserAccount
[Column("password")] [Column("password")]
[MaxLength(100)] [MaxLength(100)]
public string? Password { get; set; } public string? Password { get; set; }
public byte[] PasswordHash { get; set; }
public byte[] PasswordSalt { get; set; }
public DateTime DateCreated { get; set; } = DateTime.Now;
public string Role { get; set; } = "User";
} }

View File

@@ -1,7 +0,0 @@
namespace BlazorPolicyAuth.Models.ViewModels;
public class LoginViewModel
{
public string? UserName { get; set; }
public string? Password { get; set; }
}

View File

@@ -0,0 +1,9 @@
namespace BlazorPolicyAuth.Models.ViewModels
{
public class ServiceResponse<T>
{
public T? Data { get; set; }
public bool Success { get; set; } = true;
public string Message { get; set; } = string.Empty;
}
}

View File

@@ -0,0 +1,12 @@
using System.ComponentModel.DataAnnotations;
namespace BlazorPolicyAuth.Models.ViewModels
{
public class UserChangePassword
{
[Required, StringLength(100, MinimumLength = 5)]
public string Password { get; set; } = string.Empty;
[Compare("Password", ErrorMessage = "The password do not match")]
public string ConfirmPassword { get; set; } = string.Empty;
}
}

View File

@@ -0,0 +1,11 @@
using System.ComponentModel.DataAnnotations;
namespace BlazorPolicyAuth.Models.ViewModels;
public class UserLogin
{
[Required, EmailAddress]
public string UserName { get; set; } = string.Empty;
[Required]
public string Password { get; set; }
}

View File

@@ -0,0 +1,14 @@
using System.ComponentModel.DataAnnotations;
namespace BlazorPolicyAuth.Models.ViewModels
{
public class UserRegister
{
[Required, EmailAddress]
public string Email { get; set; } = string.Empty;
[Required, StringLength(100, MinimumLength = 5)]
public string Password { get; set; } = string.Empty;
[Compare("Password", ErrorMessage = "The password do not match.")]
public string ConfirmPassword { get; set; } = string.Empty;
}
}

View File

@@ -0,0 +1,140 @@
using BlazorPolicyAuth.Data;
using BlazorPolicyAuth.Models.Entities;
using BlazorPolicyAuth.Models.ViewModels;
using BlazorPolicyAuth.Services.AuthService;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
namespace BlazorAuth.Server.Services.AuthService;
public class AuthService : IAuthService
{
private readonly AppDbContext _context;
private readonly IConfiguration _configuration;
private readonly IHttpContextAccessor _httpContextAccessor;
public AuthService(AppDbContext context, IConfiguration configuration, IHttpContextAccessor httpContextAccessor)
{
_context = context;
_configuration = configuration;
_httpContextAccessor = httpContextAccessor;
}
public async Task<ServiceResponse<int>> Register(UserAccount user, string password)
{
if (await UserExists(user.UserName))
{
return new ServiceResponse<int> { Success = false, Message = "User already exist." };
}
CreatePasswordHash(password, out byte[] passwordHash, out byte[] passwordSalt);
user.PasswordHash = passwordHash;
user.PasswordSalt = passwordSalt;
_context.UserAccounts.Add(user);
await _context.SaveChangesAsync();
return new ServiceResponse<int> { Data = user.Id, Message = "Registration successful" };
}
public async Task<bool> UserExists(string email)
{
if (await _context.UserAccounts.AnyAsync(user => user.UserName.ToLower().Equals(email.ToLower())))
{
return true;
}
return false;
}
public async Task<ServiceResponse<string>> Login(string email, string password)
{
var response = new ServiceResponse<string>();
var user = await _context.UserAccounts.FirstOrDefaultAsync(u => u.UserName.ToLower().Equals(email.ToLower()));
if (user == null)
{
response.Success = false;
response.Message = "User not found.";
}
else if (!VeriyPasswordHash(password, user.PasswordHash, user.PasswordSalt))
{
response.Success = false;
response.Message = "Wrong password.";
}
else
{
response.Data = CreateToken(user);
}
return response;
}
public async Task<ServiceResponse<bool>> ChangePassword(int userId, string newPassword)
{
var user = await _context.UserAccounts.FindAsync(userId);
if (user == null)
{
return new ServiceResponse<bool> { Success = false, Message = "User not found." };
}
CreatePasswordHash(newPassword, out byte[] passwordHash, out byte[] passwordSalt);
user.PasswordHash = passwordHash;
user.PasswordSalt = passwordSalt;
await _context.SaveChangesAsync();
return new ServiceResponse<bool> { Data = true, Message = "Password has been changed." };
}
public int GetUserId() => int.Parse(_httpContextAccessor.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier));
public string GetUserEmail() => _httpContextAccessor.HttpContext.User.FindFirstValue(ClaimTypes.Name);
public async Task<UserAccount> GetUserByEmail(string email)
{
return await _context.UserAccounts.FirstOrDefaultAsync(u => u.UserName.Equals(email));
}
private static void CreatePasswordHash(string password, out byte[] passwordHash, out byte[] passwordSalt)
{
using var hmac = new HMACSHA512();
passwordSalt = hmac.Key;
passwordHash = hmac.ComputeHash(Encoding.UTF8.GetBytes(password));
}
private static bool VeriyPasswordHash(string password, byte[] passwordHash, byte[] passwordSalt)
{
using var hmac = new HMACSHA512(passwordSalt);
var computedHash = hmac.ComputeHash(Encoding.UTF8.GetBytes(password));
return computedHash.SequenceEqual(passwordHash);
}
private string CreateToken(UserAccount user)
{
var claims = new List<Claim>
{
new(ClaimTypes.NameIdentifier, user.Id.ToString()),
new(ClaimTypes.Name, user.UserName),
new(ClaimTypes.Role, user.Role)
};
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration.GetSection("AppSettings:Token").Value));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha512Signature);
var token = new JwtSecurityToken(
claims: claims,
expires: DateTime.Now.AddDays(1),
signingCredentials: creds);
var jwt = new JwtSecurityTokenHandler().WriteToken(token);
return jwt;
}
}

View File

@@ -0,0 +1,15 @@
using BlazorPolicyAuth.Models.Entities;
using BlazorPolicyAuth.Models.ViewModels;
namespace BlazorPolicyAuth.Services.AuthService;
public interface IAuthService
{
Task<ServiceResponse<int>> Register(UserAccount user, string password);
Task<bool> UserExists(string email);
Task<ServiceResponse<string>> Login(string email, string password);
Task<ServiceResponse<bool>> ChangePassword(int userId, string newPassword);
int GetUserId();
string GetUserEmail();
Task<UserAccount> GetUserByEmail(string email);
}