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 BlazorPolicyAuth.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> Register ( string email, string password) { if (await UserExists(email)) { return new ServiceResponse { Success = false, Message = "User already exist." }; } CreatePasswordHash(password, out byte[] passwordHash, out byte[] passwordSalt); var user = new UserAccount { UserName = email, PasswordHash = passwordHash, PasswordSalt = passwordSalt }; _context.UserAccounts.Add(user); await _context.SaveChangesAsync(); return new ServiceResponse { Data = user.Id, Message = "Registration successful" }; } public async Task UserExists(string email) { if (await _context.UserAccounts.AnyAsync(user => user.UserName.ToLower().Equals(email.ToLower()))) { return true; } return false; } public async Task> Login(string email, string password) { var response = new ServiceResponse(); 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> ChangePassword(int userId, string newPassword) { var user = await _context.UserAccounts.FindAsync(userId); if (user == null) { return new ServiceResponse { 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 { 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 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 { 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; } }