Not really major push as I start the year. Development wise I'm able to piece parts of my project which are Registration, Forgot and Reset password.
They are actually basic features of any web applications so one should set it up as soon as you start the development process.
For local email I managed to use my existing localhost setup of Mailhog in my Docker component. Here's how it works for high level flow for reset password.- Allow user to post forgot password using their email
- Generate password token and generate reset link
- Send reset password email to user
- Allow user to submit new password
For the email functionality, here's what I did.
- Added Email Service class
- Add EmailConfigOptions class
- Inject to Program.cs
- Add email config to appsettings.json which will be loaded
- Load the SMTP config to Program.cs
The Codes:
Here's my class for email.
public class EmailService{private readonly EmailServiceOptions _options;public EmailService(EmailServiceOptions options){_options = options;}public async Task SendEmailAsync(string recipient, string subject, string body){using (var client = new SmtpClient(_options.SmtpServer, _options.SmtpPort)){client.UseDefaultCredentials = false;client.Credentials = new NetworkCredential(_options.SmtpUsername,_options.SmtpPassword);using (var message = new MailMessage()){message.From = new MailAddress(_options.From);message.To.Add(recipient);message.Subject = subject;message.Body = body;await client.SendMailAsync(message);}}}}
The EmailServiceOptions model
public class EmailServiceOptions{public string SmtpServer { get; set; } = string.Empty;public int SmtpPort { get; set; } = 1025;public string SmtpUsername { get; set; } = string.Empty;public string SmtpPassword { get; set; } = string.Empty;public string From{ get; set; } = string.Empty;}
Here's how I added to Program.cs
var emailOptions = builder.Configuration.GetSection("EmailService").Get<EmailServiceOptions>();builder.Services.AddSingleton(emailOptions);builder.Services.AddTransient<EmailService>();
For the appsettings.json I added properties below:
"EmailService": {
"From": "theemailgmail@gmail.com",
"SmtpServer": "localhost",
"SmtpPort": 1025,
"SmtpUsername": "",
"SmtpPassword": ""
}
Now in my forgot-password
[HttpPost("forgot-password")]public async Task<IActionResult> ForgotPassword(ForgotPasswordVM request){// Validate request parametersif (!ModelState.IsValid){return BadRequest(ModelState);}// Check if the provided email is registeredvar user = await _userManager.FindByEmailAsync(request.EmailAddress);if (user == null){return BadRequest(new { message = "The provided email is not registered." });}// Generate a password reset tokenvar token = await _userManager.GeneratePasswordResetTokenAsync(user);// Send the password reset emailvar resetUrl = $"{request.ResetUrl}?token={HttpUtility.UrlEncode(token)}&email={HttpUtility.UrlEncode(request.EmailAddress)}";await _emailService.SendEmailAsync(request.EmailAddress, "Password reset request", $"Please reset your password by following this link: {resetUrl}");return Ok();}
So how my smtp works? Basically I just run a Docker image Mailhog here.
I won't cover setup of Docker Mailhog in this post but it's quite straightforward to follow the installation of it if you visit their site though.