using System.Collections.Generic;
public partial interface IRepository<T> where T : BaseEntity
IQueryable<T> Table { get; }
public class EfRepository<T> : IRepository<T> where T : BaseEntity
protected readonly dbContext _dbContext;
private DbSet<T> _entities;
public EfRepository(dbContext context)
this._dbContext = context;
public virtual IQueryable<T> Table
public virtual void Insert(T entity)
throw new ArgumentNullException(nameof(entity));
_dbContext.SaveChanges();
_dbContext.Entry<T>(entity).Reload();
throw new Exception(dbEx.ToString());
public virtual void Update(T entity)
throw new ArgumentNullException(nameof(entity));
_dbContext.Entry(entity).State = EntityState.Modified;
_dbContext.SaveChanges();
_dbContext.Entry<T>(entity).Reload();
throw new Exception(dbEx.ToString());
public virtual void Delete(T entity)
throw new ArgumentNullException(nameof(entity));
_dbContext.SaveChanges();
_dbContext.Entry<T>(entity).Reload();
throw new Exception(dbEx.ToString());
public interface IResetPasswordService
string GenerateToken(int userId);
TokenStructure ValidateToken(string token);
UserDetails GetUserDetailsByEmailId(string emailId);
UserDetails GetUserDetailsById(guid Id);
bool SendResetTokenEmail(string emailTo,string url,string userName);
TokenDetails GetTokenDetailsByUserId(guid userId);
void SaveToken(TokenDetails tokenDetails);
void UpdateToken(TokenDetails tokenDetails);
void SavePassword(PasswordDetails passwordDetails);
void UpdatePassword(PasswordDetails passwordDetails);
IList<PasswordDetails> GetPasswordDetailsByUserId(guid userId);
string HashedPassword(string password);
public class ResetPasswordService :IResetPasswordService
private readonly IRpository<UserDetails> _userRepository;
private readonly IRpository<PasswordDetails> _passwordRepository;
private readonly IRpository<TokenDetails> _tokenRepository;
public ResetPasswordService(IRpository<UserDetails> userRepository,
IRpository<PasswordDetails> passwordRepository,
IRpository<TokenDetails> tokenRepository){
_userRepository = userRepository;
_passwordRepository = passwordRepository;
_tokenRepository = userRepository;
private byte[] EncryptTextToMemory(string data, byte[] key, byte[] iv)
using (var ms = new MemoryStream())
using (var cs = new CryptoStream(ms, new TripleDESCryptoServiceProvider().CreateEncryptor(key, iv), CryptoStreamMode.Write))
var toEncrypt = Encoding.Unicode.GetBytes(data);
cs.Write(toEncrypt, 0, toEncrypt.Length);
private string DecryptTextFromMemory(byte[] data, byte[] key, byte[] iv)
using (var ms = new MemoryStream(data))
using (var cs = new CryptoStream(ms, new TripleDESCryptoServiceProvider().CreateDecryptor(key, iv), CryptoStreamMode.Read))
using (var sr = new StreamReader(cs, Encoding.Unicode))
public string EncryptText(string plainText) {
if (string.IsNullOrEmpty(plainText))
string encryptionPrivateKey = "273ece6f97dd844d";
using (var provider = new TripleDESCryptoServiceProvider())
provider.Key = Encoding.ASCII.GetBytes(encryptionPrivateKey.Substring(0, 16));
provider.IV = Encoding.ASCII.GetBytes(encryptionPrivateKey.Substring(8, 8));
var encryptedBinary = EncryptTextToMemory(plainText, provider.Key, provider.IV);
return Convert.ToBase64String(encryptedBinary);
public virtual string DecryptText(string cipherText)
if (string.IsNullOrEmpty(cipherText))
string encryptionPrivateKey = "273ece6f97dd844d";
using (var provider = new TripleDESCryptoServiceProvider())
provider.Key = Encoding.ASCII.GetBytes(encryptionPrivateKey.Substring(0, 16));
provider.IV = Encoding.ASCII.GetBytes(encryptionPrivateKey.Substring(8, 8));
var buffer = Convert.FromBase64String(cipherText);
return DecryptTextFromMemory(buffer, provider.Key, provider.IV);
public string GenerateToken(Guid userId,Guid tokenGuid)
var tokenStruct = new TokenStructure {
CreatedOn = DateTime.UtcNow,
var json = JsonConvert.SerializeObject(tokenStruct);
var encyptedToken = EncryptText(json);
public TokenStructure ValidateToken(string token)
var decrypted = DecryptText(token);
TokenStructure deserializedToken = (TokenStructure)JsonConvert.DeserializeObject(decrypted, typeof(TokenStructure));
var tokenDetails = GetTokenDetailsByUserId(deserializedToken.UserId);
if (tokenDetails != null)
if(String.Equals(tokenDetails.Token, deserializedToken.ID))
{ return deserializedToken;
public UserDetails GetUserDetailsByEmailId(string emailId)
var query = from q in _userRepository.Table
where q.EmailId == emailId
return query.FirstOrDefault();
public UserDetails GetUserDetailsByUserId(guid userId)
var query = from q in _userRepository.Table
return query.FirstOrDefault();
public TokenDetails GetTokenDetailsByUserId(guid userId)
var query = from q in _tokenRepository.Table
return query.FirstOrDefault();
public bool SendResetTokenEmail(string emailTo,string url,string userName)
var mimeMessage = new MimeMessage();
mimeMessage.From.Add(new MailboxAdress("Reset Password | PCTel","admin@pctel.com"));
mimeMessage.To.Add(new MailboxAdress(userName,emailTo));
mimeMessage.Subject = "Reset Password Link";
BodyBuilder bodyBuilder = new BodyBuilder();
bodyBuilder.HtmlBody = "<h1>Reset Passwor link </h1>" +
"<p>This link will be valiadte only for 1 day.Please reset your password by <a href='"+ url+"'>Clicking here</a></p>";
mimeMessage.Body = bodyBuilder.ToMessageBody();
using (var client = new SmtpClient
public void SaveToken(TokenDetails tokenDetails)
if (tokenDetails == null)
throw new ArgumentNullException(nameof(tokenDetails));
_tokenRepository.Insert(tokenDetails);
public void UpdateToken(TokenDetails tokenDetails)
if (tokenDetails == null)
throw new ArgumentNullException(nameof(tokenDetails));
_tokenRepository.Update(tokenDetails);
void SavePassword(PasswordDetails passwordDetails)
if (passwordDetails == null)
throw new ArgumentNullException(nameof(passwordDetails));
_passwordRepository.Insert(passwordDetails);
void UpdatePassword(PasswordDetails passwordDetails)
if (passwordDetails == null)
throw new ArgumentNullException(nameof(passwordDetails));
_passwordRepository.Update(passwordDetails);
IList<PasswordDetails> GetPasswordDetailsByUserId(guid userId)
var query = from q in _passwordRepository.Table
string HashedPassword(string password)
byte[] salt = new byte[128 / 8];
using (var rng = RandomNumberGenerator.Create())
string hashed = Convert.ToBase64String(KeyDerivation.Pbkdf2(
prf: KeyDerivationPrf.HMACSHA1,
numBytesRequested: 256 / 8));
public class ForgotPasswordController : ControllerBase
private readonly IResetPasswordService _resetPasswordService;
public ForgotPasswordController(IResetPasswordService resetPasswordService)
_resetPasswordService = resetPasswordService;
[Route("/api/reset-password")]
[SwaggerOperation("ResetPassword")]
[SwaggerResponse(statusCode: 400, type: typeof(ErrorResponseModel), description: "Input is invalid. Entered email address is invalid")]
[SwaggerResponse(statusCode: 404, type: typeof(ErrorResponseModel), description: "Input is valid but user not found.")]
public ActionResult ResetPassword(ResetPasswordDto resetPasswordDto)
var userDetails = new UserDetails();
userDetails = _resetPasswordService.GetUserDetailsByEmailId(resetPasswordDto.EmailId);
var tokenGuid = Guid.NewGuid();
string token = _resetPasswordService.GeneratToken(userDetails.UserId,tokenGuid);
var tokenDetails = new TokenDetails();
tokenDetails.Token = tokenGuid;
tokenDetails.UserId = userDetails.Id;
tokenDetails.ExpireDateTime =DateTime.UtcNow.AddHours(24);
tokenDetails.TokenStatusId = (int) TokenStatus.Active;
_resetPasswordService.SaveToken(tokenDetails);
var userName = userDetails.FirstName + " " + userDetails.LastName;
var url = "http://localhost:53058/ForgotPassword/api/reset-password/validate-token?token="+ token;
var isEmailSend = _resetPasswordService.SendResetTokenEmail(emailTo : resetPasswordDto.Email,url : url, userName : userName);
return OK("Email sent sucessful!");
return StatusCode(500,"Internal server error");
return NotFound("User not found!");
return BadRequest("Invalid email input");
return StatusCode(500,"Internal server error");
[Route("/api/reset-password/validate-token/{token}")]
[SwaggerOperation("ValidateUser")]
[SwaggerResponse(statusCode: 400, type: typeof(ErrorResponseModel), description: "Received token is invalid.")]
[SwaggerResponse(statusCode: 401, type: typeof(ErrorResponseModel), description: "Unauthorized.")]
[SwaggerResponse(statusCode: 404, type: typeof(ErrorResponseModel), description: "Input is valid but user not found.")]
public ActionResult ValidateUser(string token)
var tokenStructure = _resetPasswordService.ValidateToken(token);
if(tokenStructure != null)
var tokenDetails = _resetPasswordService.GetTokenDetailsByUserId(tokenStructure.UserId);
int TimeCompare = DateTime.Compare(tokenDetails.ExpireDateTime, DateTime.UtcNow);
if((TimeCompare == -1 || TimeCompare == 0) && TimeCompare.TokenStatusId == (int)TokenStatus.Active)
return StatusCode(500,"Internal server error");
[Route("/api/reset-password/update-password")]
[SwaggerOperation("UpdatePassword")]
[SwaggerResponse(statusCode: 400, type: typeof(ErrorResponseModel), description: "Password should contain special character.")]
[SwaggerResponse(statusCode: 404, type: typeof(ErrorResponseModel), description: "Input is valid but user not found.")]
public ActionResult UpdatePassword(UpdatePasswordDto updatePasswordDto)
var userId = _resetPasswordService.ValidateToken(token);
var userDetails = _resetPasswordService.GetUserDetailsByUserId(userId);
var passwords = _resetPasswordService.GetPasswordDetailsByUserId(userId);
if(passwords.Count() != 0)
foreach(var password in passwords)
password.PasswordStatusId = (int)PasswordStatus.Expired;
_resetPasswordService.UpdatePassword(password);
var newPassword = new PasswordDetails();
var hashedPassword = _resetPasswordService.HashedPassword(updatePasswordDto.Password);
newPassword.Password = hashedPassword;
newPassword.UserId = userDetails.Id;
newPassword.PasswordStatusId = (int) PasswordStatus.Active;
_resetPasswordService.SavePassword(newPassword);
var tokenDetails = _resetPasswordService.GetTokenDetailsByUserId(userId);
tokenDetails.TokenStatusId = (int) TokenStatus.Expired;
_resetPasswordService.UpdateToken(tokenDetails);
else{return NotFound("User not found!");}
else{return NotFound("User not found!");}
return StatusCode(500,"Internal server error");
return BadRequest("Invalid input");