UserManager.SendEmailAsync hangs

asp.net-mvc, asp.net-mvc-5, asynchronous, c#

Solution

This Works for me

ApplicationUserManager constructor:

public AppUserManager(IUserStore<AppUser> store)
        : base(store)
{
        this.UserTokenProvider = new TotpSecurityStampBasedTokenProvider<AppUser, string>();
        this.EmailService = new EmailService();
}

EmailService:

public class EmailService : IIdentityMessageService
{
    public Task SendAsync(IdentityMessage message)
    {
        // Credentials:
        var credentialUserName = ConfigurationManager.AppSettings["emailFrom"];
        var sentFrom = ConfigurationManager.AppSettings["emailFrom"];
        var pwd = ConfigurationManager.AppSettings["emailPassword"];

        // Configure the client:
        System.Net.Mail.SmtpClient client =
            new System.Net.Mail.SmtpClient("smtp-mail.outlook.com");

        client.Port = 587;
        client.DeliveryMethod = System.Net.Mail.SmtpDeliveryMethod.Network;
        client.UseDefaultCredentials = false;

        // Create the credentials:
        System.Net.NetworkCredential credentials =
            new System.Net.NetworkCredential(credentialUserName, pwd);

        client.EnableSsl = true;
        client.Credentials = credentials;

        // Create the message:
        var mail =
            new System.Net.Mail.MailMessage(sentFrom, message.Destination);

        mail.Subject = message.Subject;
        mail.Body = message.Body;

        // Send:
        return client.SendMailAsync(mail);
    }
}

IMPORTANT If you are using external mail provider you should revise your external applications configurations for example: gmail : Allowing less secure apps to access your account

ForgotPassword method in AccountController

    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> RecoveryPassword(LoginInfoModel model)
    {
        if (ModelState.IsValid)
        {
            var user = await UserManager.FindByNameAsync(model.Email);
            if (user == null || !(await UserManager.IsEmailConfirmedAsync(user.Id)))
            {
                // Don't reveal that the user does not exist or is not confirmed
                return View("ForgotPasswordConfirmation");
            }

            var code = await UserManager.GeneratePasswordResetTokenAsync(user.Id);
            var callbackUrl = Url.Action("ResetPassword", "Account",
            new { UserId = user.Id, code = code }, protocol: Request.Url.Scheme);
            await UserManager.SendEmailAsync(user.Id, "Reset Password",
            "Reinicia tu contraseña clicando : <a href=\"" + callbackUrl + "\">aqui</a>");
            return View("ForgotPasswordConfirmation");
        }

        // If we got this far, something failed, redisplay form
        return View(model);
    }

And finally I see my e-mail on my mailbox

Problem

I have a custom user manager with a custom EmailService. I'm calling the UserManager.SendEmailAsync() method in the AccountController, and it just hangs. I even tried to use it with invalid SMTP hostname, then an exception occurs and in debug I can see it goes into the catch block, but regardless the return View(model) statement, it just "hangs" in the browser and keeps on loading forever. Any ideas? ApplicationUserManager constructor: ``` public ApplicationUserManager(IUserStore<ApplicationUser> store) : base(store) { EmailService = new EmailService(); } ``` EmailService: ``` public class EmailService : IIdentityMessageService { public async Task SendAsync(IdentityMessage message) { // Credentials: string smtpServer = ConfigurationManager.AppSettings["EmailSmtpServer"]; int smtpPort = int.Parse(ConfigurationManager.AppSettings["EmailSmtpPort"]); bool enableSsl = bool.Parse(ConfigurationManager.AppSettings["EmailEnableSSL"]); string smtpUsername = ConfigurationManager.AppSettings["EmailSmtpUsername"]; string smtpPassword = ConfigurationManager.AppSettings["EmailSmtpPassword"]; string sentFrom = ConfigurationManager.AppSettings["EmailSentFrom"]; // Configure the client: var client = new SmtpClient(smtpServer, Convert.ToInt32(587)); client.Port = smtpPort; client.DeliveryMethod = SmtpDeliveryMethod.Network; client.UseDefaultCredentials = false; client.EnableSsl = enableSsl; // Create the credentials: var credentials = new NetworkCredential(smtpUsername, smtpPassword); client.Credentials = credentials; // Create the message: var mail = new System.Net.Mail.MailMessage(sentFrom, message.Destination); mail.Subject = message.Subject; mail.Body = message.Body; // Send: await client.SendMailAsync(mail); } } ``` ForgotPassword method in AccountController ``` // // POST: /Account/ForgotPassword [HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] public async Task<ActionResult> ForgotPassword(ForgotPasswordViewModel model) { if (ModelState.IsValid) { var user = await UserManager.FindByNameAsync(model.Email); // Don't check confirmation status for now //if (user == null || !(await UserManager.IsEmailConfirmedAsync(user.Id))) if (user == null) { ModelState.AddModelError("", "The user either does not exist or is not confirmed."); return View(); } // For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=320771 // Send an email with this link string code = await UserManager.GeneratePasswordResetTokenAsync(user.Id); var callbackUrl = Url.Action("ResetPassword", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme); try { await UserManager.SendEmailAsync(user.Id, "Reset Password", "Please reset your password by clicking <a href=\"" + callbackUrl + "\">here</a>"); } catch (Exception ex) { ModelState.AddModelError("", ex.Message); return View(model); } return RedirectToAction("ForgotPasswordConfirmation", "Account"); } // If we got this far, something failed, redisplay form return View(model); } ```

Original source