Emails using Mailgun in ASP.NET Core

Updated version of this post can be found here.

At last month’s Nashville .Net Users Group meeting Michael McCann when over some of the aspects of ASP.NET’s membership provider (non-core version). One of the things he talked about was enabling email as part of the user sign up process and for use in password recovery. This post is going to cover the same emailing aspect but in ASP.NET core using mailgun to actually send emails.

Account Controller

In the account controller most of the code needed is already present and just needs to be uncommented. In the Register function uncomment the following which will send the user an email asking that the address be confirmed. This of course stops users from signing up with email addresses they don’t actually have access to.

var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
var callbackUrl = Url.Action("ConfirmEmail", 
                             "Account", 
                             new { userId = user.Id, code = code }, 
                             protocol: HttpContext.Request.Scheme);
await _emailSender.SendEmailAsync(model.Email, "Confirm your account",
  "Please confirm your account by clicking this link: <a href=\"" + callbackUrl + "\">link</a>");

And then comment out this next line which would sign the user in before they have used the email above to confirm their account.

//await _signInManager.SignInAsync(user, isPersistent: false);

Next in the Login function add the following bit of code just before the call to _signInManager.PasswordSignInAsync. This looks up the user by email address and returns an error if the account has not been confirmed.

var user = await _userManager.FindByNameAsync(model.Email);
if (user != null)
{
    if (!await _userManager.IsEmailConfirmedAsync(user))
    {
        ModelState.AddModelError(string.Empty, 
                                 "You must have a confirmed email to log in.");
        return View(model);
    }
}

The last change is in the ForgotPassword function. Uncomment the following code to send the user an email to reset their password.

var code = await _userManager.GeneratePasswordResetTokenAsync(user);
var callbackUrl = Url.Action("ResetPassword", 
                             "Account", 
                             new { userId = user.Id, code = code }, 
                             protocol: HttpContext.Request.Scheme);
await _emailSender.SendEmailAsync(model.Email, "Reset Password",
 "Please reset your password by clicking here: <a href=\"" + callbackUrl + "\">link</a>");
return View("ForgotPasswordConfirmation");

Forgot Password View

In ForgotPassword.cshtml uncomment the following section to show the UI associated with email based password reset.

<form asp-controller="Account" asp-action="ForgotPassword" method="post" class="form-horizontal" role="form">
    <h4>Enter your email.</h4>
    <hr />
    <div asp-validation-summary="ValidationSummary.All" class="text-danger"></div>
    <div class="form-group">
        <label asp-for="Email" class="col-md-2 control-label"></label>
        <div class="col-md-10">
            <input asp-for="Email" class="form-control" />
            <span asp-validation-for="Email" class="text-danger"></span>
        </div>
    </div>
    <div class="form-group">
        <div class="col-md-offset-2 col-md-10">
            <button type="submit" class="btn btn-default">Submit</button>
        </div>
    </div>
</form>

Caution for existing sites

With the changes above if a user has not confirmed their email address then they will not be able to log in or reset their password. Any existing users would need to have their accounts marked as confirmed manually by updating the EmailConfirmed bit field in the AspNetUsers table or be provided away to confirm their account.

Mailgun

Mailgun is an email service that provides a simple API for sending emails and allows up to 10,000 emails to be sent free every month. I have only used mailgun for sending test emails so I can’t speak to how it holds up at scale.

After signing up for an account click on the domains tab and select the only existing active domain which should start with something like sandbox.

Storing Configuration

In my project I created an EmailSettings class that will be loaded from user secrets in the start up of the application. For more details on general configuration in ASP.NET Core check out this post and thenthis post for more details on user secrets. The following is my email settings class.

public class EmailSettings
{
    public string ApiKey { get; set; }
    public string BaseUri { get; set; }
    public string RequestUri { get; set; }
    public string From { get; set; }
}

If using mailgun the above fields map to the following from the mailgun domain page.

EmailSettings Mailgun Example
ApiKey API Key key-*
BaseUri API Base URL https://api.mailgun.net/v3/
RequestUri API Base URL sandbox*.mailgun.org
From Default SMTP Login postmaster@sandbox*.mailgun.org

A couple of notes to the above table on what I actually saved in my config files.

EmailSettings Field Note Example
ApiKey Used with basic auth and needs username api:key-*
RequestUri Needs the API end point to call sandbox*.mailgun.org/messages

The following is what my actual config files ends up looking like.

{
  "EmailSettings": {
    "ApiKey": "api:key-*",
    "BaseUri": "https://api.mailgun.net/v3/",
    "RequestUri": "sandbox*.mailgun.org/messages",
    "From": "postmaster@sandbox*.mailgun.org"
  }
}

In the ConfigureServices function Startup.cs I added a reference to the new settings class so it would be available for dependency injection.

services.Configure<EmailSettings>(Configuration.GetSection("EmailSettings"));

Message Services

In the Services folder there is a MessageServices.cs file which contains the AuthMessageSender class that has an empty implementation for sending email base on an IEmailSender interface which defines a single SendEmailAsync method. This function is already being called in the code that was uncommented above so I am going to use it to call mailgun’s API.

First I need to get the email settings defined above injected into the AuthMessageSenderClass by adding a class level field and a constructor. The only thing the constructor is doing is saving a reference to the injected settings class.

private readonly EmailSettings _emailSettings;

public AuthMessageSender(IOptions<EmailSettings> emailSettings)
{
    _emailSettings = emailSettings.Value;
}

Next is the SendEmailAsync function mentioned above which I changed to an async function and added the code to send an email using mailgun’s API.

public async Task SendEmailAsync(string email, string subject, string message)
{
    using (var client = new HttpClient { BaseAddress = new Uri(_emailSettings.BaseUri) })
    {
        client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic",
            Convert.ToBase64String(Encoding.ASCII.GetBytes(_emailSettings.ApiKey)));

        var content = new FormUrlEncodedContent(new[]
        {
            new KeyValuePair<string, string>("from", _emailSettings.From),
            new KeyValuePair<string, string>("to", email),
            new KeyValuePair<string, string>("subject", subject),
            new KeyValuePair<string, string>("text", message)
        });

        await client.PostAsync(_emailSettings.RequestUri, content).ConfigureAwait(false);
    }
}

This code is using the HttpClient to send a request to mailgun’s API using basic authorization and form url encoded content to pass the API the relevant bit of information.

With that your application will now email account conformations and password resets.

Other Email Options

Mailgun is obviously not the only option for sending emails. This post from Mashape lists 12 API providers. In addition SMTP is also an option which this post by Steve Gordon covers.

16 thoughts on “Emails using Mailgun in ASP.NET Core”

  1. Can you put a link to a published solution, or email me a zip to the solutoin, leaving the default mailgun keys like you documented.

    I am having issues being new to asp.net and would like to compare generated files to your steps.
    This could be an issue with core 1.0 ver 3 or some auto configuration on your default visual studio 2015.

    I will be happy to share with you the comparison results and insert the extra steps in your document.

    Thank you for publishing your article.

    1. Unfortunately I can’t locate the code I used for this entry. What issues are you having and I can see if I can help? Maybe even upload your code and share it with me and I will see if I can help you track down the issues you are having.

  2. I have email you a zip file with the complete solution that I could not resolve.
    The other approach is to try to use the sample asp.net core 1 web sample and make the changes as indicated in your article. You may have selected better libraries than I did.

  3. Recommend edits 08/28/2016:

    In project.json add to dependencies – Check version# can change
    “Microsoft.Net.Http”: “2.2.29”,
    “System.Net.Http”: “4.1.0”,
    “System.Text.Encoding”: “4.0.11”

    In MessageService.cs add
    using System.Net.Http;
    using System.Net.Http.Headers;
    using System.Text;
    using Microsoft.Extensions.Options;

    If you have a Mailgun account and need to code to a “real domain name” then the recommend
    the format of values to connect have changed as indicated.

    In secrets.json example
    {
    “EmailSettings”: {
    “ApiKey”: “api:key-*”,
    “BaseUri”: “https://api.mailgun.net/v3/DOMAIN”,
    “RequestUri”: “https://api.mailgun.net/v3/DOMAIN/messages”,
    “From”: “postmaster@DOMAIN”
    }
    }
    * ” for the rest of your mailgun apikey”
    DOMAIN “for your full domain name”
    MailGun provides API Base URL for Base URI
    The value for RequestUri is crafted.

    When your done there are 5 files that changed from VS generated code.
    AccountController.cs
    MessageServices.cs
    ForgotPassword.cshtml
    project.json
    Startup.cs

    You can get the sample at:
    https://github.com/ascottpond/mailgunexample-ASP.NET-Core-1.0

    one more example to refer to that was helpful but for prior v2 of mailgun
    https://gist.github.com/duncansmart/3777530

    1. Thank you for providing an updated working sample! That will be helpful to future readers. One of the super challenging things about the ASP.NET Core space is that blog posts from pre RTM are out of date quickly.

      It is also the reason I have been revisiting some topics now that RTM is out. I will add this one to the list of things to look back at.

    2. I downloaded project through github and added secrets.json file with my Mailgun credentials like you explained. I am able to register but I am not getting any confirmation email.

      1. Does the email show from with in Mailgun? I would also step through and make sure you are getting 200 response from the request being sent to Mailgun’s API. If not that could provide a clue as to why it isn’t working.

        1. Now confirmation email is sent to the email address. It works perfectly. But when I click the confirmation email link it goes to the webpage and shows error which is
          ->
          An error occurred while processing your request.
          Development Mode
          Swapping to Development environment will display more detailed information about the error that occurred.

          Development environment should not be enabled in deployed applications, as it can result in sensitive information from exceptions being displayed to end users. For local debugging, development environment can be enabled by setting the ASPNETCORE_ENVIRONMENT environment variable to Development, and restarting the application.

          Thanks beforehand

          1. Great news you were able to get the first email.
            Some notes that I should have added in notes.

            First time you run and do a register a new email address you could get an error to causes you to create the sql local db. After fixing that thru a migration button with the error. Try again

            The second time you the process runs without error but you see no email for a while. Mailgun in its doc states this is normal for new accounts that do not have send history. Mine showed up 30 minutes after I send the email.

            G to the Mailgun website and view your domain send mail count number and in my case went from 0 to 1, other tests incremented that number.

            User secrets was one of the configuration methods as coded Eric was leveraging the generated code.
            Had I deployed to a website server process, I would have to wire up the sql database connection string, and create the tables , and a different configuration method for email sending, same values different spot.

            The ApiKey value used is crafted with the prepend of “api:” to the Mailgun API Key value.

            The code generated by Visual Studio 2015 v3 had a the email response build string different than Eric.
            So I change this line to match what Eric did AccountController.cs
            Step 1
            $”Please confirm your account by clicking this link: link“);
            Step 4
            “Please reset your password by clicking here: link“);
            Before I made those two changes the link in the email was not able to run.

            What I learned:
            Visual Studio 2015 v3 can generate code that has issues. Perhaps this is why that code was remarked out from the released example. by having the instructions had us in-remark that code and use it, was to make a demo about sending email with Mailgun as easy as possible, Ie use a crafted email by the process.

            I expect future versions of Visual Studio 2015 asp.net core 1 generated code examples to get enhancements/ changes. For example the message part would have inline table with a button and the link material hidden. or better yet, the ability to pull templates for HTML created email. What we got was a quick dirty proof of concept. That’s why the email was in text form not HTML.

            Eric really handled 2 different concepts, one is User Secrets and write about sending email using asp.net core 1.0, By taking that information I have just added some extra notes and a working development example where all you needed was a Mailgun account and populate User Secrets correctly.

            When I tested, after creating a new user, I used my msn.com email account and left the debugging session open that created it. Waited for the email, then clicked on the link and got.

            Confirm Email.
            Thank you for confirming your email. Please Click here to Log in.

            © 2016 – mailgunexample

            If one looks at the generated link I have no idea how that converts to knowing I am running on a local host test website and converting the host name perhaps there some api process running to handle it. there is more to learn.

            As stated before my development goal was just to prove sending an email using asp.net core 1.0 with Mailgun running/debugging the rest of the asp.net example was not in scope.

  4. How to make it work to send html emails. I tried replacing form[“text”] with form[“html”] = message; but doesnt send html email

    1. Rajiv sorry you are having issues with html emails. My computer is currently hosed and I will be unable to get you answer in a reasonable time.

      Maybe some else will be able to answer. I plan to rewrite this post and I will add html email as something to cover.

      If you find a solution please share it with us.

      1. I found the answer to this question. My mistake was I wasn’t doing this:
        HttpUtility.HtmlDecode(message) and hence it wasnt sending as HTML. However after doing this it works. Thanks for your response though!

  5. Pingback: ASP.NET Blog

  6. Pingback: .NET Blog

Leave a Reply to [email protected] Cancel Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.