28

I am using the following code to try to send an email asynchronously, but no email is sent and I am not sure what is being done incorrectly. I have also added the 2nd segment of code in the web.config for the emailing protocol.

SendEmailAsync code

await UserManager.SendEmailAsync(username.Id, "MTSS-B: Forgot Password", "Here is your new password. Please go back to the MTSS-B web tool and sign in. You will be prompted to create your own password.<br/><br/>" + tmpPass + "<br/><br/>MTSS-B Administrator");

Web.config code

<system.net>
<mailSettings>
  <smtp>
    <network host="smtp1.airws.org" userName="" password="" />
  </smtp>
</mailSettings>
</system.net>

****UPDATE****

I tested if an email could be sent with the usual method and an email was able to be sent using the following code.

MailMessage m = new MailMessage(new MailAddress(ConfigurationManager.AppSettings["SupportEmailAddr"]), new MailAddress(model.Email));
m.Subject = "MTSS-B: Forgot Password"; 
m.Body = string.Format("Here is your new password. Please go back to the MTSS-B web tool and sign in. You will be prompted to create your own password.<br/><br/>Password: " + tmpPass + "<br/><br/>MTSS-B Administrator"); 
m.IsBodyHtml = true;
SmtpClient smtp = new SmtpClient("smtp2.airws.org"); 
smtp.Send(m);
Sun Sun Ku
  • 337
  • 1
  • 6
  • 17

5 Answers5

53

In your app you probably have a file called IdentityConfig.cs in the App_Start folder. That file probably has something like this towards the top:

public class EmailService : IIdentityMessageService
{
    public Task SendAsync(IdentityMessage message)
    {
        // Plug in your email service here to send an email.
        return Task.FromResult(0);
    }
}

change it to:

public class EmailService : IIdentityMessageService
{
    public Task SendAsync(IdentityMessage message)
    {
        SmtpClient client = new SmtpClient();
        return client.SendMailAsync(ConfigurationManager.AppSettings["SupportEmailAddr"], 
                                    message.Destination, 
                                    message.Subject, 
                                    message.Body);
    }
}

Customizing the send code to your liking.

Joe
  • 3,664
  • 25
  • 27
18

Joe's solution guided me a lot, thanks for that! Yet to contribute with it, you must include the following namespaces:

using System.Configuration;
using System.Net.Mail;

I´ve changed his solution a little, after a lot of trying i've reached some code that worked (it must still be refacored, but it doesn't change the idea), this is how my SendAsync method looks like:

public Task SendAsync(IdentityMessage message) {
    //SmtpClient client = new SmtpClient();
    //return client.SendMailAsync(ConfigurationManager.AppSettings["SupportEmailAddr"],
    //                            message.Destination,
    //                            message.Subject,
    //                            message.Body);

    SmtpClient client = new SmtpClient();
    client.Port = 587;
    client.Host = "smtp.gmail.com";
    client.EnableSsl = true;
    //client.Timeout = 10000;
    client.DeliveryMethod = SmtpDeliveryMethod.Network;
    client.UseDefaultCredentials = false;
    client.Credentials = new NetworkCredential("mailName@gmail.com", "mailPassword");

    return client.SendMailAsync("mailName@gmail.com", message.Destination, message.Subject, message.Body);
}

You can see Joe's solution commented in top, then, a lot of configurations are made to the SmtpClient (the timeout is commented due to some testing i've been making, you can uncomment it if it suits your needs).

After that, the mail is sent in an asyncronous way, notice that the sender (which Joe get's from an AppSettings variable) is the same specified in the credentials (you must create a gmail [or wathever mail service you want] account and use it's name and password to create the credentials).

That should do the trick! Bear in mind that gmail may complicate your life while trying to connect to your new mail account this way, to solve it, you must log in that account and go to your account configurations and activate "less secure applications acces" (or sth like that, i speak spanish so my traduction may not be that good...).

Edit 22/04/16: Seems this solution doesn't work properly while working behing a proxy, there should be a way to configure it. In my case i find it cheaper to disable the proxy and go on, but for those of you who can't afford that, expect to have this obstacle while implementing this.

Alvaro Rodriguez Scelza
  • 3,643
  • 2
  • 32
  • 47
  • Can you explain what you did in terms of proxy issues you faced? My solution through gmail works fine when I run it from my local machine but when I publish the code to Azure I get **502 - Web server received an invalid response while acting as a gateway or proxy server.** and I am wondering if that may be related to your issues. Thanks! – hvaughan3 Oct 10 '16 at 13:08
  • I'm afraid it isn't, We have a proxy in my work, and that was the one making trouble, I ended up never receiving the mails while it was up, until I disabled it for testing, but the problem was all before publishing to azure. Sadly, I don't remember having trouble with azure at the moment of deployment. [link]http://stackoverflow.com/questions/21135784/502-web-server-received-an-invalid-response-while-acting-as-a-gateway-or-proxy That guy seemed to have the same problem, which apparently was focused on the connection string. I would suggest you start from there :/ – Alvaro Rodriguez Scelza Oct 11 '16 at 21:19
  • Thanks for getting back to me! Will check out the link. – hvaughan3 Oct 12 '16 at 03:33
9

I think you are using Macrosoft ASP.NET Identity and SMTP email client server. Then your full configuration would be like below :

Web.config

<system.net>
<mailSettings>
  <smtp from="xyz@gmail.com">
    <network host="smtp.gmail.com" userName="xyz" defaultCredentials="false" password="xyz" port="587" enableSsl="true" />
  </smtp>
</mailSettings>
</system.net>

Create a class SmtpEmailService.cs

public class SmtpEmailService : IIdentityMessageService
{
    readonly ConcurrentQueue<SmtpClient> _clients = new ConcurrentQueue<SmtpClient>();

    public async Task SendAsync(IdentityMessage message)
    {
        var client = GetOrCreateSmtpClient();
        try
        {
            MailMessage mailMessage = new MailMessage();

            mailMessage.To.Add(new MailAddress(message.Destination));
            mailMessage.Subject = message.Subject;
            mailMessage.Body = message.Body;

            mailMessage.BodyEncoding = Encoding.UTF8;
            mailMessage.SubjectEncoding = Encoding.UTF8;
            mailMessage.IsBodyHtml = true;

            // there can only ever be one-1 concurrent call to SendMailAsync
            await client.SendMailAsync(mailMessage);
        }
        finally
        {
            _clients.Enqueue(client);
        }
    }


    private SmtpClient GetOrCreateSmtpClient()
    {
        SmtpClient client = null;
        if (_clients.TryDequeue(out client))
        {
            return client;
        }

        client = new SmtpClient();
        return client;
    }
}

IdentityConfig.cs

// Configure the application user manager used in this application. 
//UserManager is defined in ASP.NET Identity and is used by the application.
public class ApplicationUserManager : UserManager<User>
{
    public ApplicationUserManager(IUserStore<User> store, IIdentityMessageService emailService)
        : base(store)
    {
        this.EmailService = emailService;
    }

    public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
    {
        var manager = new ApplicationUserManager(new UserStore<User>(context.Get<ApplicationDbContext>()), new SmtpEmailService());
        .
        .
        .
        .
        return manager;
    }
}

If you are using dependency injection(DI) then configure it. I am using UnityContainer (UnityConfig.cs) so my configuration is :

container.RegisterType<IIdentityMessageService, SmtpEmailService>();

Finally use it from your controller :

public async Task<IHttpActionResult> TestSmtpMail()
{
    var subject = "Your subject";
    var body = "Your email body it can be html also";
    var user = await UserManager.FindByEmailAsync("xxx@gmail.com");
    await UserManager.SendEmailAsync(user.Id, subject, body);
    return Ok();
}

You can get an error something like :

The SMTP server requires a secure connection or the client was not authenticated.

Then Allow less security apps & Allow gmail account making it possible for other apps to gain access

For more details and SendGrid client visit here

Arif
  • 6,094
  • 4
  • 49
  • 81
0

Great. Thank you. I have error because ConfigurationManager.AppSettings["SupportEmailAddr"] was null. You have to set it in your web.config file. You have a section called: <appSettings>. That is what ConfigurationManager.AppSettings is referring too. ["SupportEmailAddr"] is looking at a specific setting called SupportEmailAddr. In your web.config it would look something like this:

<appSettings>
    <add key="SupportEmailAddr" value="someone@example.com" />
</appSettings>
Goran Belačić
  • 81
  • 2
  • 10
0

Using the Out-The-Box build of a VS2017, C# Web Application with Webforms and Individual Accounts, I found this comment help me resolve the NULL value returned from the ConfigurationManager.AppSettings call.

I confirmed the mail settings were all correct but I could not get a valid result from the call to the ConfigurationManager to retrieve the string value. It always returned NULL.

I found that if you use the Application Settings (ProjectName -> Properties -> Settings.Settings) in the Solution Explorer tree, you will always get a NULL returned because the section inside the Web.Config file will not exist, but the section does and has a different syntax.

Just remember to put the section before the final closing tag.

Simple and elegant, thanks Goran.