using DevExpress.Office.Crypto; using DevExpress.Xpo.DB; using DevExpress.XtraPrinting; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Newtonsoft.Json.Linq; using Microsoft.IdentityModel.Tokens; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Security.Cryptography; using System.Text.Json; using TIAMWebApp.Shared.Application.Models; using TIAMWebApp.Shared.Application.Models.PageModels; using TIAMWebApp.Server.Models; using System.Text; using AyCode.Core.Logger; using Microsoft.AspNetCore.Hosting; using Microsoft.EntityFrameworkCore; using TIAM.Database.DataLayers.Users; using AyCode.Utils.Helpers; using TIAM.Entities.Users; using TIAM.Models.Dtos.Users; using TIAMWebApp.Server.ModelsTIAMWebApp.Shared.Application.Models; using TIAMWebApp.Shared.Application.Utility; using TIAM.Database.DataLayers.Admins; using System; using TIAM.Entities.Profiles; using TIAM.Entities.Addresses; namespace TIAMWebApp.Server.Controllers { [Authorize] [ApiController] [Route("api/v1/[controller]")] public class UserAPIController : ControllerBase { private UserDal _userDal; private AdminDal _adminDal; private readonly IConfiguration _configuration; private readonly IWebHostEnvironment _webHostEnvironment; readonly PasswordHasher _hasher = new(); /*private UserModel[] users = new UserModel[] { new UserModel(new Guid("540271f6-c604-4c16-8160-d5a7cafedf00"), "test@tiam.hu", "+36701234567", "Asdasd123456"), new UserModel(new Guid("4cbaed43-2465-4d99-84f1-c8bc6b7025f7"), "adam@tiam.hu", "+36701234567", "Asdasd987654") };*/ private readonly ILogger _logger; public UserAPIController(ILogger logger, IConfiguration configuration, IWebHostEnvironment webHostEnvironment, UserDal userDal, AdminDal adminDal) { _logger = logger; _configuration = configuration; _webHostEnvironment = webHostEnvironment; _userDal = userDal; _adminDal = adminDal; } [AllowAnonymous] [HttpPost] [Route(APIUrls.AuthenticateUserRouteName)] public async Task AuthenticateUser([FromBody] JsonElement SerializedLoginModel) { Console.WriteLine("AuthenticateUser called"); var authenticateUser = JObject.Parse(SerializedLoginModel.GetRawText()).ToObject(); //check if userModel exists //var userModel = await _userManager.FindByNameAsync(authenticateUser.UserName); //if (userModel == null) return Unauthorized(); Console.WriteLine(authenticateUser.Email); //var dbUser = await GetUserByEmail(authenticateUser.Email); var dbUser = await _userDal.GetUserByEmailAsync(authenticateUser.Email); //check if password is valid //bool isValidUser = await _userManager.CheckPasswordAsync(userModel, authenticateUser.Password); //mocking if (dbUser is null) { return Unauthorized(); } else { var isValidUser = false; if (dbUser.Password == authenticateUser.Password) { Console.WriteLine("Password is valid"); isValidUser = true; } if (isValidUser) { Console.WriteLine("UserModel authenticated, let's start JWT"); var accessToken = GenerateAccessToken(dbUser); Console.WriteLine("Generate refresh token"); var refreshToken = GenerateRefreshToken(); dbUser.RefreshToken = refreshToken; //Update userModel with refreshToken!! await _userDal.UpdateJwtRefreshTokenAsync(dbUser.EmailAddress, dbUser.RefreshToken); var response = new MainResponse { Content = new AuthenticationResponse { RefreshToken = refreshToken, AccessToken = accessToken }, IsSuccess = true, ErrorMessage = "" }; return Ok(response); } else { Console.WriteLine("User not valid"); return Unauthorized(); } } } private string GenerateAccessToken(User user) { var tokenHandler = new JwtSecurityTokenHandler(); Console.WriteLine("----------------------------------------------------------"); if (_configuration["JWT:Key"] == null) throw new SecurityTokenException("Token is null"); var keyDetail = Encoding.UTF8.GetBytes(_configuration["JWT:Key"] ?? string.Empty); Console.WriteLine(_configuration["JWT:Key"]); var claims = new List { new(ClaimTypes.NameIdentifier, user.Id.ToString()), new(ClaimTypes.Email, user.EmailAddress) }; var tokenDescriptor = new SecurityTokenDescriptor { Audience = _configuration["JWT:Audience"], Issuer = _configuration["JWT:Issuer"], Expires = DateTime.UtcNow.AddMinutes(30), Subject = new ClaimsIdentity(claims), SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(keyDetail), SecurityAlgorithms.HmacSha256Signature) }; var token = tokenHandler.CreateToken(tokenDescriptor) as JwtSecurityToken; var writtenToken = tokenHandler.WriteToken(token); Console.WriteLine(writtenToken); return writtenToken; } [AllowAnonymous] [HttpPost] [Route(APIUrls.RefreshTokenRouteName)] public async Task RefreshToken(RefreshTokenRequest? refreshTokenRequest) { Console.WriteLine("RefreshToken called"); var response = new MainResponse(); if (refreshTokenRequest is null) { Console.WriteLine("RefreshTokenRequest is null"); response.ErrorMessage = "Invalid request"; return BadRequest(response); } var principal = GetPrincipalFromExpiredToken(refreshTokenRequest.AccessToken); if (principal != null) { Console.WriteLine("Principal is not null"); var email = principal.Claims.FirstOrDefault(f => f.Type == ClaimTypes.Email); //var userModel = await _userManager.FindByEmailAsync(email?.Value); //UserModel? user = users.FirstOrDefault(x => x.Email == email?.Value); User? dbUser = null; if (email != null) { //get user from db dbUser = await _userDal.GetUserByEmailAsync(email.Value); Console.WriteLine("DbUser email: " + dbUser?.EmailAddress); } //mocking - update userModel with new refreshToken so it returns true after the check below //dbUser.RefreshToken = refreshTokenRequest.RefreshToken; if (dbUser is null || dbUser.RefreshToken != refreshTokenRequest.RefreshToken) { response.ErrorMessage = "Invalid Request"; Console.WriteLine($"{dbUser?.RefreshToken}, {refreshTokenRequest.RefreshToken}"); return BadRequest(response); } var newAccessToken = GenerateAccessToken(dbUser); var refreshToken = GenerateRefreshToken(); //mocking - update userModel with new refreshToken dbUser.RefreshToken = refreshToken; //TODO await _userManager.UpdateAsync(userModel); await _userDal.UpdateJwtRefreshTokenAsync(dbUser.EmailAddress, dbUser.RefreshToken); response.IsSuccess = true; response.Content = new AuthenticationResponse { RefreshToken = refreshToken, AccessToken = newAccessToken }; return Ok(response); } Console.WriteLine("Principal is null"); return NotFound("Invalid Token Found"); } private ClaimsPrincipal GetPrincipalFromExpiredToken(string token) { var tokenHandler = new JwtSecurityTokenHandler(); if (_configuration["JWT:Key"] == null) throw new SecurityTokenException("Token is null"); var keyDetail = Encoding.UTF8.GetBytes(_configuration["JWT:Key"] ?? string.Empty); var tokenValidationParameter = new TokenValidationParameters { ValidateIssuer = false, ValidateAudience = false, ValidateLifetime = false, ValidateIssuerSigningKey = true, ValidIssuer = _configuration["JWT:Issuer"], ValidAudience = _configuration["JWT:Audience"], IssuerSigningKey = new SymmetricSecurityKey(keyDetail), }; var principal = tokenHandler.ValidateToken(token, tokenValidationParameter, out var securityToken); if (securityToken is not JwtSecurityToken jwtSecurityToken || !jwtSecurityToken.Header.Alg.Equals(SecurityAlgorithms.HmacSha256, StringComparison.InvariantCultureIgnoreCase)) throw new SecurityTokenException("Invalid token"); return principal; } private string GenerateRefreshToken() { var randomNumber = new byte[32]; using (var rng = RandomNumberGenerator.Create()) { rng.GetBytes(randomNumber); return Convert.ToBase64String(randomNumber); } } [AllowAnonymous] [HttpPost] [Route("CreateUser")] public async Task CreateUser([FromBody] JsonElement SerializedRegistrationModel) { Console.WriteLine("CreateUser called"); if (string.IsNullOrEmpty(SerializedRegistrationModel.GetRawText())) { return BadRequest("SerializedLoginModel is required"); } else { var user = JObject.Parse(SerializedRegistrationModel.GetRawText()).ToObject(); if (user != null) { //add userModel to users array //Array.Resize(ref users, users.Length + 1); //users[users.Length - 1] = new UserModel(user.Email, user.PhoneNumber, user.Password); var userId = Guid.NewGuid(); var email = user?.Email; var phoneNumber = user?.PhoneNumber; var password = user?.Password; if (email is null || phoneNumber is null || password is null) { return BadRequest("Invalid request"); } else { Console.WriteLine($"User to be created: {userId}"); Console.WriteLine($"User to be created: {email}"); Console.WriteLine($"User to be created: {phoneNumber}"); Console.WriteLine($"User to be created: {password}"); await _userDal.CreateUserAsync(new User(userId, email, phoneNumber, password)); } } return Ok("yes"); } } [AllowAnonymous] [HttpPost] [Route(APIUrls.CreateGuestUserRouteName)] public async Task CreateGuestUser([FromBody] JsonElement SerializedRegistrationModel) { Console.WriteLine("CreateGuestUser called"); bool result = false; UserModelDtoDetail? guestUser = null; if (string.IsNullOrEmpty(SerializedRegistrationModel.GetRawText())) { return BadRequest("SerializedLoginModel is required"); } else { var user = JObject.Parse(SerializedRegistrationModel.GetRawText()).ToObject(); if (user != null) { Random random = new Random(); string chars = "1234567890"; string nameExtension = new string(Enumerable.Repeat(chars, 10) .Select(s => s[random.Next(s.Length)]).ToArray()); var userId = Guid.NewGuid(); var email = user?.Email; var phoneNumber = user?.PhoneNumber; var password = user?.Password; Guid? referralId = user?.ReferralId; if (email is null || phoneNumber is null || password is null) { return BadRequest("Invalid request"); } else { Console.WriteLine($"User to be created: {userId}"); Console.WriteLine($"User to be created: {email}"); Console.WriteLine($"User to be created: {phoneNumber}"); Console.WriteLine($"User to be created: {password}"); User userToCreate = new(userId, email, phoneNumber, password); userToCreate.Profile = new Profile(); userToCreate.Profile.Name = "Guest-" + nameExtension; userToCreate.RefferalId = referralId; Random rnd = new Random(); userToCreate.Profile.Address = new Address(); userToCreate.Profile.Address.AddressText = "NAN"; userToCreate.Profile.Address.Latitude = Math.Round(90 + rnd.NextDouble(), 8); userToCreate.Profile.Address.Longitude = Math.Round(180 + rnd.NextDouble(), 8); result = await _adminDal.AddUser(userToCreate); guestUser = await _userDal.GetUserModelDtoDetailByIdAsync(userId); } } return Ok(guestUser); } } [HttpPost] [Route("Test1")] public async Task TestEndpoint([FromBody] int testParam) { return Ok(testParam.ToString()); } [HttpGet] [Route("Test2")] public string TestEndpoint2(int testParam) { return testParam.ToString(); } [AllowAnonymous] [HttpGet] [Route("GetUsers")] public Task> GetUsers() { //var users = await _userDal.Ctx.Users.ToListAsync();//.GetUsersAsync(); //return users; return _userDal.GetAllUsersModelDtoAsync(); } [AllowAnonymous] [HttpPost] [Route("GetUserByEmail")] public Task GetUserByEmail(string email) { Logger.Info($"GetUserByEmail called with email: {email}"); Console.WriteLine($"GetUserByEmail called with email: {email}"); return _userDal.GetUserModelDtoByEmailAsync(email); } [AllowAnonymous] [HttpPost] [Route("GetUserById")] public Task GetUserById([FromBody] Guid id) { Logger.Info($"GetUserById called with id: {id}"); return _userDal.GetUserModelDtoByIdAsync(id); } [AllowAnonymous] [HttpPost] [Route("GetUserDetailById")] public Task GetUserDetailById([FromBody] Guid id) { Logger.Info($"GetUserDetailById called with id: {id}"); return _userDal.GetUserModelDtoDetailByIdAsync(id); } private bool VerifyPassword(string password, string hashedPassword) { var isPasswordValid = _hasher.VerifyPassword(password, hashedPassword); return isPasswordValid; } private string HashPassword(string password) { var hashedPassword = _hasher.HashPassword(password); return hashedPassword; } } }