récupération de classes
This commit is contained in:
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="Migrations\" />
|
<Folder Include="Migrations\" />
|
||||||
|
<Folder Include="Services\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -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";
|
||||||
}
|
}
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
namespace BlazorPolicyAuth.Models.ViewModels;
|
|
||||||
|
|
||||||
public class LoginViewModel
|
|
||||||
{
|
|
||||||
public string? UserName { get; set; }
|
|
||||||
public string? Password { get; set; }
|
|
||||||
}
|
|
||||||
9
BlazorPolicyAuth/Models/ViewModels/ServiceResponse.cs
Normal file
9
BlazorPolicyAuth/Models/ViewModels/ServiceResponse.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
12
BlazorPolicyAuth/Models/ViewModels/UserChangePassword.cs
Normal file
12
BlazorPolicyAuth/Models/ViewModels/UserChangePassword.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
11
BlazorPolicyAuth/Models/ViewModels/UserLogin.cs
Normal file
11
BlazorPolicyAuth/Models/ViewModels/UserLogin.cs
Normal 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; }
|
||||||
|
}
|
||||||
14
BlazorPolicyAuth/Models/ViewModels/UserRegister.cs
Normal file
14
BlazorPolicyAuth/Models/ViewModels/UserRegister.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
140
BlazorPolicyAuth/Services/AuthService/AuthService.cs
Normal file
140
BlazorPolicyAuth/Services/AuthService/AuthService.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
15
BlazorPolicyAuth/Services/AuthService/IAuthService.cs
Normal file
15
BlazorPolicyAuth/Services/AuthService/IAuthService.cs
Normal 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);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user