Search code examples
c#asp.netasp.net-coresmtpmailgun

SMTP server unexpectedly disconnected error when sending emails using mailgun service and MailKit in ASP.NET Core


I'm facing an issue when trying to send emails using the MailKit library in an ASP.NET Core application. The emails are sent successfully on my local machine, but when I deploy the application to a remote server (IIS), I encounter the following exception:

Exception: Failed to send the email: The SMTP server has unexpectedly disconnected. EmailSender Class:

using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.Extensions.Options;
using MimeKit;
using MimeKit.Text;
using System;
using System.Threading.Tasks;
using MailKit.Security;

namespace syngri_web_app.Services
{
    public class EmailSender : IEmailSender
    {
        private readonly MailgunSettings _mailgunSettings;

        public EmailSender(IOptions<MailgunSettings> mailgunSettings)
        {
            _mailgunSettings = mailgunSettings.Value;
        }

        public async Task SendEmailAsync(string email, string subject, string htmlMessage)
        {
            try
            {
                var mimeMessage = new MimeMessage();
                mimeMessage.From.Add(new MailboxAddress("Excited Admin", "foo@" + _mailgunSettings.Domain));
                mimeMessage.To.Add(new MailboxAddress("Excited User", email));
                mimeMessage.Subject = subject;
                mimeMessage.Body = new TextPart(TextFormat.Html)
                {
                    Text = htmlMessage
                };

                using (var client = new MailKit.Net.Smtp.SmtpClient())
                {
                    client.ServerCertificateValidationCallback = (s, c, h, e) => true;

                    await client.ConnectAsync("smtp.mailgun.org", 587, SecureSocketOptions.StartTls);
                    client.AuthenticationMechanisms.Remove("XOAUTH2");

                    await client.AuthenticateAsync("postmaster@"+_mailgunSettings.Domain+"", _mailgunSettings.ApiKey);

                    await client.SendAsync(mimeMessage);
                    await client.DisconnectAsync(true);
                }
            }
            catch (Exception ex)
            {
                // Log the exception details
                Console.WriteLine("An exception occurred while sending the email: " + ex.ToString());
                if (ex.InnerException != null)
                {
                    Console.WriteLine("Inner Exception: " + ex.InnerException.ToString());
                }
                throw new Exception("Failed to send the email: " + ex.Message);
            }
        }
    }
}

Controller Code:

        [HttpPost]
        [AllowAnonymous]

        public async Task<IActionResult> Forget(UserDto request)
        {

            if (ModelState.IsValid)
            {
                var user = await userManager.FindByEmailAsync(request.Email);
                if (user != null && !user.EmailConfirmed)
                {
                    ModelState.AddModelError("message", "Email not confirmed yet");
                    ItoastNotification.Error("Something went Wrong");
                    return View(request);

                }
                if (user == null) {
                    ModelState.AddModelError("message", "Email not Exist");
                    ItoastNotification.Error("Something went Wrong, Please inter valid email");
                    return View(request);
                }
                var code = await userManager.GeneratePasswordResetTokenAsync(user);
                code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
                var callbackUrl = Url.Action(
                    "ResetPassword",
                    "Account",
                    new { area = "", code, request.Email },
                    protocol: Request.Scheme
                );
                var emailMessage = $"Please reset your password by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.";
                await emailSender.SendEmailAsync(request.Email, "Reset Password", emailMessage);

                ItoastNotification.Success("Reset Email Has been Sent Please Check your Email");
                return RedirectToAction("Login");
            }
            foreach (var error in ModelState.Values.SelectMany(v => v.Errors))
            {
                // Handle or log the validation error messages
                var errorMessage = error.ErrorMessage;
                ItoastNotification.Error("Something went Wrong: "+errorMessage);
                break;
            }
            
            return View(request);
        }

I've verified that the SMTP server settings are correct, and the emails are successfully sent from my local machine. However, the issue arises only when deploying to the remote server.


Solution

  • I resolved my issue by updating my emailSender class with the following code:

    var smtpClient = new SmtpClient("smtp.mailgun.org")
    {
        Port = 587,
        Credentials = new NetworkCredential(_mailgunSettings.UserName, _mailgunSettings.Password),
        EnableSsl = false
    };
    
    var mailMessage = new MailMessage
    {
        From = new MailAddress("[email protected]"),
        Subject = subject,
        Body = formattedHtmlMessage,
        IsBodyHtml = true
    };
    
    mailMessage.To.Add(email);
    
    await smtpClient.SendMailAsync(mailMessage);