UserManager.SendEmailAsync hangs

10,191

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

Mailbox

Share:
10,191
Adam Szabo
Author by

Adam Szabo

Updated on June 04, 2022

Comments

  • Adam Szabo
    Adam Szabo almost 2 years

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