User Secrets

Email with ASP.NET Core Using Mailgun

Sending emails from ASP.NET Core using Mailgun is a topic I covered in this post almost a year ago. The previous post was before ASP.NET Core hit 1.0 and I didn’t save or upload the code to GitHub. Based on the comments on the post I decided to redo the post using the current version of ASP.NET.

Starting point

For this post, I created a new ASP.NET Core Web Application targeting .NET Core using Individual User Accounts for authentication. The project before any changes for email can be found here.

UI and Controller change to support email

The project template comes with all the UI and controller functions need to support email, but they are commented out. The following is going to walk through uncommenting the proper code.

Account controller

Starting with the Register function the code to send a confirmation email needs to be uncommented and the existing call to _signInManager.SignInAsync should be commented out to keep a user from being signed in before their email address has been confirmed. The following is after the changes.

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>");
//await _signInManager.SignInAsync(user, isPersistent: false);

Next, in the Login function add a check to verify a user’s account has been confirmed before allowing them to sign in. The new code starts with var user = await _userManager.FindByNameAsync(model.Email); the code above it is just to provide context.

 public async Task<IActionResult> Login(LoginViewModel model, 
                                        string returnUrl = null)
 {
      ViewData["ReturnUrl"] = returnUrl;
      if (ModelState.IsValid)
      {
          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);
              }
          }

Finally, in the ForgotPassword function uncomment the following to enable sending the user a password reset link.

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

To enabled the UI related to sending an email for a forgotten password open ForgotPassword.cshtml found in the Views/Account/ directory and uncomment the following.

<form asp-controller="Account" asp-action="ForgotPassword" 
      method="post" class="form-horizontal">
    <h4>Enter your email.</h4>
    <hr />
    <div asp-validation-summary="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>

Warning for sites with existing users

The changes above will cause issues for any existing users since they will not have completed the email confirmation step keeping them from being able to log in or reset passwords. Manually marking existing users as confirmed can be done by updating the EmailConfirmed bit field to true in the AspNetUsers table.

Mailgun

Mailgun is an email service run by Rackspace that provides a simple API for sending emails. The free level of the service allows up to 10k emails to be sent a month.

You can sign up for an account here. Once logged in go to the Domains section.

Next, select your domain should only be one if you are on a new account. This will take you to a screen that looks like the following some of which will be needed to connect with the Mailgun API. I took the time to replace my information with a fake version so this screen shot could be referenced using the values from the screenshot for the rest of the post.

Configuration

Settings class

In order to hold and load Mailgun email related settings add a new EmailSettings class. In the sample project, this class can be found in the Configuration directory. The following is the full contents of the file.

public class EmailSettings
{
    public string ApiKey { get; set; }
    public string ApiBaseUri { get; set; }
    public string RequestUri { get; set; }
    public string From { get; set; }
}
User secrets introduction

User secrets is a concept in ASP.NET Core used to set configuration items and have them stored outside of the project so they will be excluded from version control. They are a great way to store private API key and related items which is why they will be used to store our Mailgun configuration items. I will be coving the basics here, but for a more detail explanation check out the official docs on the topic of app secrets.

Setting user secrets

In the Solution Explorer right-click on the project and select Manage User Secrets.

This will open the secrets.json file which will be used to store secrets related to the select project. Keep in mind this file is stored in your user directory in an unencrypted way so don’t view it as a secured store.

Based on the screenshot above from Mailgun’s domain detail page the json file would look like the following. The RequestUri is the only setting not pulled from the domain settings above and would just need fakesandbox replaced with the sandbox ID for your domain.

{
  "EmailSettings": {
    "ApiKey": "api:key-fakeapikey",
    "ApiBaseUri": "https://api.mailgun.net/v3/",
    "RequestUri": "fakesandbox.mailgun.org/messages",
    "From": "[email protected]"
  }
}
Loading user secrets in Startup

In the ConfigureServices function of the Startup class the EmailSettings section of our user secrets can be loaded and made available via the dependency injection system using the following line of code.

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

Not that user secrets are only meant to be used for development and for a production build of the applications the settings would need to be moved to a different location such as environment variables or Azure Key Vault.

Using Mailgun to send email

Not that the application has the email sending portion of the UI enabled the SendEmailAsync function of the AuthMessageSender class needs to be implemented. The class can be found in the MessageServices.cs file of the Services directory.

Injection of email settings

The first change needed is to add a class level variable to store email settings and to add a constructor that will allow the email setting to be injected.

private readonly EmailSettings _emailSettings;

public AuthMessageSender(IOptions<EmailSettings> emailOptions)
{
    _emailSettings = emailOptions.Value;
}
Sending an email

The body of the SendEmailAsync function is where the call to Mailgun’s API will be made using the email setting injected via the class’s constructor. The following is the full body of the function.

using (var client = new HttpClient { BaseAddress = 
                                     new Uri(_emailSettings.ApiBaseUri) })
{
    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>("html", message)
    });

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

The Mailgun API key is sent as an authentication header value with the rest of the parameters being sent via form URL encoded content. Finally, the Request URI from the email settings is used to send the post request to Mailgun.

If you just need to send a plain text instead of HTML the “html” key can be replaced with “text”.

Authorized recipients required for test domain

When using Mailgun with the default test domain note that only emails addressed to Authorized Recipients will be delivered. To add a recipient click Authorized Recipients button on Mailgun’s Domains page.

This will take you to the Authorized Recipients page where you can use the Invite New Recipient button to add a recipient.

Enter the email address you want to add and click the Send Invite button. After the email address is confirmed mail to that address will be delivered. Keep in mind this is only for test accounts and doesn’t have to be done when being used with a real domain.

Using logs to verify state of emails

During my testing, I wasn’t seeing emails come through and I thought something was wrong with my code, but it turned out that Mailgun was getting the request to send the mail they just hadn’t be processed yet. The Logs section of your Mailgun account is helpful for determining if they are getting your request to send an email or not.

As you can see in the screenshot the email request was accepted, but not yet delivered. It was 10 minutes later before the email was actually delivered. I am not sure if this delay is just for test domains or if would apply to live ones as well.

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.

Wrapping up

The code finished code that goes with this post can be found here. Thank you to all the commenters on the original post for stepping in when I didn’t have all the answer or code available.

Email with ASP.NET Core Using Mailgun Read More »

SMS using Twilio Rest API in ASP.NET Core

A couple of weeks ago I went over using email in ASP.NET Core which left the provided MessageService class half implemented.  This post is going to cover the implementation of the other MessageService function that is used to send SMS as part of two-factor authentication.

View

In Views/Manage/Index.cshtml uncomment the following to enable the UI bit associated with phone numbers.

@(Model.PhoneNumber ?? "None")
    @if (Model.PhoneNumber != null)
    {
        <br />
        <text>[&nbsp;&nbsp;<a asp-controller="Manage" asp-action="AddPhoneNumber">Change</a>&nbsp;&nbsp;]</text>
        <form asp-controller="Manage" asp-action="RemovePhoneNumber" method="post" role="form">
            [<button type="submit" class="btn-link">Remove</button>]
        </form>
    }
    else
    {
        <text>[&nbsp;&nbsp;<a asp-controller="Manage" asp-action="AddPhoneNumber">Add</a>&nbsp;&nbsp;]</text>
    }

And this as well.

@if (Model.TwoFactor)
    {
        <form asp-controller="Manage" asp-action="DisableTwoFactorAuthentication" method="post" class="form-horizontal" role="form">
            Enabled [<button type="submit" class="btn-link">Disable</button>]
        </form>
    }
    else
    {
        <form asp-controller="Manage" asp-action="EnableTwoFactorAuthentication" method="post" class="form-horizontal" role="form">
            [<button type="submit" class="btn-link">Enable</button>] Disabled
        </form>
    }

Twilio

I spend a lot of time trying to find a services that allows sending of SMS for free and had zero luck. I ended up going with  Twilio as they do provide free messaging with their trial account. The usage section of the web site will make it looking like you will be changed, but that is just to provide an idea of what the service would cost and will not actually be charged.

Storing Configuration

Just as a couple of weeks ago for EmailSetting I created a SmsSettings class that will be loaded from user secrets in the StartUp class of the application. For more details on general configuration in ASP.NET Core check out this post and then this post for more details on user secrets. The following is my SMS settings class.

public class SmsSettings
{
    public string Sid { get; set; }
    public string Token { get; set; }
    public string BaseUri { get; set; }
    public string RequestUri { get; set; }
    public string From { get; set; }
}

And this is the config file looks like with the curly braces needed to be replace with values from your Twilio account. For example if your Twilio phone number was 15554447777 then the from line would be: “From”: “+15554447777”

{
  "SmsSettings": {
    "Sid": "{TwilioAccountSid}",
    "Token": "{TwilioAuthToken}",
    "BaseUri": "https://api.twilio.com",
    "RequestUri": "/2010-04-01/Accounts/{TwilioAccountSid}/Messages.json",
    "From": "+{TwilioPhoneNumber}"
  }
}

Then in ConfigureServices function of Startup.cs add a reference to the SmsSettings class to make it available using dependency injection.

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

Message Services

In Services/MessageService.cs there is an empty implementation for sending SMS base on ISmsSender which defines a single SendSmsAsync function which is called when the application wants to send a SMS.

Add a constructor to the class if it doesn’t already have one so that the SmsSettings can be injected by the framework and add a field to store the settings in. I have removed the email related items from the constructor but you can look at this post if you want to include the email related bits a well.

private readonly SmsSettings _smsSettings;

public AuthMessageSender(IOptions<SmsSettings> smsSettings)
{
    _smsSettings = smsSettings.Value;
}

Then the SendSmsAsync function which uses the HttpClient with basic authentication and form url encoded content to make a post request to the Twilio API looks like the following.

public async Task SendSmsAsync(string number, string message)
{
    using (var client = new HttpClient { BaseAddress = new Uri(_smsSettings.BaseUri) })
    {
        client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic",
            Convert.ToBase64String(Encoding.ASCII.GetBytes($"{_smsSettings.Sid}:{_smsSettings.Token}")));

        var content = new FormUrlEncodedContent(new[]
        {
            new KeyValuePair<string, string>("To",$"+{number}"),
            new KeyValuePair<string, string>("From", _smsSettings.From),
            new KeyValuePair<string, string>("Body", message)
        });

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

Now you application is capable of sending SMS.

ASP.NET Docs

As I was writing this I came across Rick Anderson’s post in the official docs that covers two-factor authentication. I highly recommend you read Rick’s post as he covers the UI portion in more depth than I did. Another note Rick is using the Twilio helper client were I am using the HttpClient in order to maintain dnxcore50 compatibility.

SMS using Twilio Rest API in ASP.NET Core Read More »