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));
}
}
}
}