using Asp.Versioning; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; using OnlineSalesAutoCrop.CoreAPI; using OnlineSalesAutoCrop.CoreAPI.Models; using OnlineSalesAutoCrop.CoreAPI.Models.Global; using OnlineSalesAutoCrop.CoreAPI.Models.Requests.Integrations; using OnlineSalesAutoCrop.CoreAPI.Models.Requests.MobileApp; using OnlineSalesAutoCrop.CoreAPI.Models.Responses; using OnlineSalesAutoCrop.CoreAPI.Models.Responses.Integrations; using OnlineSalesAutoCrop.CoreAPI.Models.Responses.MobileApp; using OnlineSalesAutoCrop.CoreAPI.Services.Contracts.Auth; using OnlineSalesAutoCrop.CoreAPI.Services.Contracts.Systems; using System; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Text; using System.Threading.Tasks; namespace OnlineSalesAutoCrop.CoreAPI.Controllers { /// /// /// /// /// /// /// /// /// /// /// [Authorize] [ApiController] [ApiVersion("1.0")] [ValidateAntiForgeryToken] [Route("api/mobile/v{version:apiVersion}/auth")] public class MobileAuthController(IUserService service, IOptions appSettings, IEaseCache cache, ILogger logger, IRefreshTokenService refreshTokenService) : ControllerBase { private readonly ILogger _logger = logger; private readonly IEaseCache _cache = cache; private readonly IUserService _service = service; private readonly IRefreshTokenService _refreshTokenService = refreshTokenService; private readonly AppSettings _appSettings = appSettings?.Value; private readonly DateTimeOffset _options = Helper.CreateEaseCacheOptions(); /// /// Login using your credential data retrieve from SqlServer /// /// /// /// /// If login successful ValidUser: true /// If login successful Return ValidUser: true and UserName: not empty [HttpPost("Login")] [AllowAnonymous] [IgnoreAntiforgeryToken] [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(MobileAppAuthRequest))] public async Task Login([FromBody] MobileAppAuthRequest request) { ArgumentNullException.ThrowIfNull(request); AppAuthUserResponse loginReponse = new(); if (string.IsNullOrEmpty(request.LoginId)) { string message = "Login ID is required."; return BadRequest(MobileResponseBase.Failure(message, StatusCodes.Status400BadRequest)); } if (string.IsNullOrEmpty(request.Password)) { string message = "Password is required."; return BadRequest(MobileResponseBase.Failure(message, StatusCodes.Status400BadRequest)); } string ipAddress = string.Empty; try { #region Decrypt LoginID string cipherSecretKey = GlobalFunctions.ConvertFromBase64String(_appSettings.CipherSecretKey); #endregion bool checkPwd = true; ipAddress = Request.HttpContext.GetIpAddress(); loginReponse = await _service.LoginAsync(request: request, ipAddress: ipAddress); if (loginReponse == null || loginReponse.UserId == 0) { string message = "Login ID/Password is invalid.\" : \"You are not Authorized to login into the System."; return Unauthorized(MobileResponseBase.Failure(message, StatusCodes.Status401Unauthorized)); } if (loginReponse.LoginStatus == EnumLoginStatus.Unsuccessful) { string message = checkPwd ? $"Login ID/Password is invalid for {request.LoginId}." : "You are not Authorized to login into the System"; return Unauthorized(MobileResponseBase.Failure(message, StatusCodes.Status401Unauthorized)); } if (loginReponse.IsLocked) { string message =$"Your Account is locked . Please try again after few minutes"; return Unauthorized(MobileResponseBase.Failure(message, StatusCodes.Status401Unauthorized)); } if (!loginReponse.IsAuthorized ) { string message = $"You are not Authorized to Login into the System, Please contact with System Administrator."; return Unauthorized(MobileResponseBase.Failure(message, StatusCodes.Status401Unauthorized)); } string pwdSecretKey = GlobalFunctions.ConvertFromBase64String(_appSettings.PwdSecretKey); string userPwd = Ease.NetCore.Utility.Global.CipherFunctions.EncryptByAES(privateKey: pwdSecretKey, publicKey: pwdSecretKey, data: request.Password); byte[] key = Encoding.ASCII.GetBytes(_appSettings.JwtCryptoKey); JwtSecurityTokenHandler tokenHandler = new(); var tokenDescriptor = new SecurityTokenDescriptor { Subject = new ClaimsIdentity( [ Helper.CreateClaim("LoginId", loginReponse.LoginId), Helper.CreateClaim("Email", loginReponse.Email), Helper.CreateClaim("Mobile", $"{loginReponse.MobileNumber}"), Helper.CreateClaim("HashKey", Guid.NewGuid().ToString()) ]), Expires = DateTime.UtcNow.AddMinutes(_appSettings.AccessTokenDuration), SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha512Signature) }; SecurityToken token = tokenHandler.CreateToken(tokenDescriptor); string userToken = tokenHandler.WriteToken(token); GenerateRefreshTokenRequest refreshTokenRequest = new GenerateRefreshTokenRequest() { UserId = loginReponse.UserId, IpAddress = ipAddress, RawRefreshToken = string.Empty }; var refreshToken = await _refreshTokenService.GenerateRefreshTokenByUserAsync(refreshTokenRequest); //If token length is greater than or equal to 4096 (4KB) then return error //because cookie can not store more than 4KB data and we are storing this token in cookie for authentication if (userToken.Length >= 4096) //4Kb { string message = $"Authentication Token is too large for cookie."; return StatusCode(StatusCodes.Status431RequestHeaderFieldsTooLarge, MobileResponseBase.Failure(message, StatusCodes.Status431RequestHeaderFieldsTooLarge)); } loginReponse.LoginId = loginReponse.LoginId; loginReponse.AccessToken = userToken; loginReponse.RefreshToken = refreshToken.RefreshToken; loginReponse.AccessTokenExpiryTime = DateTime.UtcNow.AddHours(12); loginReponse.RefreshTokenExpiryTime = refreshToken.ExpireTime; return Ok(MobileResponseBase.Success(loginReponse)); } catch (Exception ex) { string msg = $"{request?.LoginId}~{ipAddress}"; _logger.LogError(exception: ex, message: msg); string mesgae= ex.InnerException != null ? ex.InnerException.Message : ex.Message; return StatusCode(StatusCodes.Status500InternalServerError, MobileResponseBase.Failure(mesgae, StatusCodes.Status500InternalServerError)); } } /// /// Login using your credential data retrieve from SqlServer /// /// /// /// /// If login successful ValidUser: true /// If login successful Return ValidUser: true and UserName: not empty [HttpPost("RefreshToken")] [AllowAnonymous] [IgnoreAntiforgeryToken] [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(MobileRrefreshTokenResponse))] public async Task GetRefreshToken([FromBody] IntegrationRefreshTokenRequest request) { MobileRrefreshTokenResponse response = new(); try { var userRefreshToken = await _refreshTokenService.GetUserByRefreshTokenAsync(request.RefreshToken); if (!userRefreshToken.IsActive) { string msg = $"Refresh Token is not active: {request?.RefreshToken}"; _logger.LogError(message: msg); return Unauthorized( MobileResponseBase.Failure(msg, StatusCodes.Status401Unauthorized)); } byte[] key = Encoding.ASCII.GetBytes(_appSettings.JwtCryptoKey); JwtSecurityTokenHandler tokenHandler = new(); var tokenDescriptor = new SecurityTokenDescriptor { Subject = new ClaimsIdentity( [ Helper.CreateClaim("LoginId", userRefreshToken.LoginId), Helper.CreateClaim("Email", userRefreshToken.EmailAddress), Helper.CreateClaim("AuthKey", $"{userRefreshToken.AuthKey}"), Helper.CreateClaim("HashKey", Guid.NewGuid().ToString()) ]), Expires = DateTime.UtcNow.AddHours(12), SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha512Signature) }; SecurityToken token = tokenHandler.CreateToken(tokenDescriptor); string userToken = tokenHandler.WriteToken(token); string ipAddress = Request.HttpContext.GetIpAddress(); var refreshToken = await _refreshTokenService.GenerateRefreshTokenAsync(request.RefreshToken, ipAddress); response.AccessToken = userToken; response.RefreshToken = refreshToken; return Ok(MobileResponseBase.Success(response)); } catch (Exception ex) { string msg = $"{request?.RefreshToken}"; _logger.LogError(exception: ex, message: msg); return StatusCode(StatusCodes.Status500InternalServerError, MobileResponseBase.Failure(ex.InnerException != null ? ex.InnerException.Message : ex.Message, StatusCodes.Status500InternalServerError)); } } /// /// Login using your credential data retrieve from SqlServer /// /// /// /// /// If login successful ValidUser: true /// If login successful Return ValidUser: true and UserName: not empty [HttpPost("Logout")] [Authorize] [IgnoreAntiforgeryToken] [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(AuthLogoutResponse))] public async Task Logout([FromBody] AuthLogoutRequest request) { AuthLogoutResponse response = new(); try { string ipAddress = Request.HttpContext.GetIpAddress(); response = await _service.LogoutAsync(request, ipAddress); return Ok(MobileResponseBase.Success(response, "Successfully Logout")); } catch (Exception ex) { string msg = $"Exception occur on logout {request?.LoginId}"; _logger.LogError(exception: ex, message: msg); return StatusCode(StatusCodes.Status500InternalServerError, MobileResponseBase.Failure(ex.InnerException != null ? ex.InnerException.Message : ex.Message, StatusCodes.Status500InternalServerError)); } } /// /// Login using your credential data retrieve from SqlServer /// /// /// /// /// If login successful ValidUser: true /// If login successful Return ValidUser: true and UserName: not empty [HttpPost("ChangePassword")] [Authorize] [IgnoreAntiforgeryToken] [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(AppChangePasswordResponse))] public async Task ChangePassword([FromBody] AppUserChangePasswordRequest request) { AppChangePasswordResponse response = new(); try { if(request.Password.Equals(request.ConfirmPassword)) { string msg = $"You can't use your old password as your new password."; _logger.LogError(message: msg); return BadRequest(MobileResponseBase.Failure(msg, StatusCodes.Status400BadRequest)); } string ipAddress = Request.HttpContext.GetIpAddress(); response = await _service.ChangePasswordAsync(request, ipAddress); return Ok(MobileResponseBase.Success(response, "Successfully Changed the Password")); } catch (Exception ex) { string msg = $"Exception occur on logout {request?.LoginId}"; _logger.LogError(exception: ex, message: msg); return StatusCode(StatusCodes.Status500InternalServerError, MobileResponseBase.Failure(ex.InnerException != null ? ex.InnerException.Message : ex.Message, StatusCodes.Status500InternalServerError)); } } } }