How to send ASP.NET Core Identity emails with Twilio SendGrid

Niels Swimburger.NET 🍔 - Mar 16 '22 - - Dev Community

This blog post was written for Twilio and originally published at the Twilio blog.

As part of ASP.NET Identity, ASP.NET Core provides a bunch of features for account management, authentication, and authorization. ASP.NET Identity also sends emails to confirm email addresses and reset passwords, but it's up to you to implement how those emails are sent.

In this tutorial, you will learn how to send ASP.NET Identity emails using Twilio SendGrid.

Prerequisites

You will need these things to follow along:

You can find the source code for this tutorial on GitHub. Use it if you run into any issues, or submit an issue, if you run into problems.

Configure your SendGrid account to send emails

There are two things you need to configure before you can send emails: First, you’ll need to set up Sender Authentication. This will verify that you own the email address or domain that you will send emails from. Second, you’ll need to create a SendGrid API Key with permission to send emails.

Sender Authentication

To quickly get started, you will use Single Sender Verification for this tutorial. This verifies that you own the email address that the application will send emails from. Single Sender Verification is great for testing purposes , but it is not recommended for production.

Twilio recommends Domain Authentication for production environments. An authenticated domain proves to email providers that you own the domain, and removes the "via sendgrid.net" text that inbox providers would otherwise append to your from address.

To set up Single Sender Verification, click the Settings tab in the left menu. Once the settings tab opens, click Sender Authentication.

Side-navigation with a Settings group and multiple links in the group. The link "Sender Authentication" is highlighted.

Then, click Get Started under the Single Sender Verification section.

A button saying "Get Started" to verify an individual email address.

This opens a form in the right-side panel. Fill out the form with your information and email address.

A form to create a sender. The form asks for contact information including the email address that will be used to send emails from.

Click Create after filling out the form. Another panel will appear on the right, asking you to confirm your email address in your email inbox.Go to your personal email inbox, open the email from SendGrid, and click Verify Single Sender.

An email from SendGrid with a button saying "Verify Single Sender".

Your email address has been verified. You can now use it to send emails!

Create a SendGrid API key to send emails

Back on the SendGrid website, click API Keys under the Settings tab , then click Create API Key in the top right-hand corner. This opens another form in the right-side panel.

Give your API Key a useful name. You can assign different permissions to the API Key. For optimal security, you should only give the minimum amount of permissions that you need.

Next, click Restricted Access.

A form to create an API Key. The form asks for a name for the key and what permissions to grant to the key.

Scroll down to the Mail Send accordion item and click on it to reveal the permissions underneath. Drag the slider to the right for the Mail Send permission.

A list of permissions that you can control using a slider bar next to each permission. The "Mail Send" accordion item is expanded to reveal the "Mail Send" permission underneath.

Scroll to the bottom of the form and click Create & View. The API key will now be displayed on your screen.

You will not be able to retrieve the API key again once you leave this screen, so make sure you copy the API Key somewhere safe.

API key is displayed to copy and store elsewhere. Key will not be shown again.

With the sender verified and the API Key created, you’re ready to write some code!

Send Identity emails using Twilio SendGrid

You'll be running a couple commands in this tutorial, but if you prefer using an IDE, there should be equivalent actions for Visual Studio and JetBrains Rider. Open your preferred shell to follow along with the provided commands.

If you already have a web application with ASP.NET Core Identity, skip the next step. In your shell, run the following .NET CLI command to create the project:

dotnet new webapp -o WebAppIdentity -au Individual
Enter fullscreen mode Exit fullscreen mode

This command will generate an ASP.NET Core project with Identity and put it in the WebAppIdentity folder.

Then, navigate to the new project directory in your shell:

cd WebAppIdentity
Enter fullscreen mode Exit fullscreen mode

If you would start the project now, and try to register a new account, the web application will tell you that an email sender has not been configured. Also, it will display a link that would normally be sent to your inbox, to confirm your email, to simulate the process.

The Register confirmation page which would normally tell you to confirm your email by going to your mail inbox, but now simply provides the confirmation link on the page.

However, the real page will be shown when you register the email sender. To do this, you have to first create a class that implements the IEmailSender interface, and then register the class in ASP.NET Core's dependency injection container.

To do this, create a new file in the project's root directory, named SendGridEmailSender.cs, with the following code:

using Microsoft.AspNetCore.Identity.UI.Services;

namespace WebAppIdentity;

public class SendGridEmailSender : IEmailSender
{
  public async Task SendEmailAsync(string toEmail, string subject, string message)
  {
    throw new NotImplementedException();
  }
}
Enter fullscreen mode Exit fullscreen mode

The SendGridEmailSender implements the IEmailSender interface. The interface only expects you to implement one method, SendEmailAsync. This method will be used by ASP.NET Identity to send your emails.

To implement SendEmailAsync method using SendGrid, you'll first need to install the SendGrid library for C# and .NET. Head back to your shell and run the following command to install the library via the SendGrid NuGet package:

dotnet add package SendGrid
Enter fullscreen mode Exit fullscreen mode

Update the SendGridEmailSender.cs file with the following code:

using Microsoft.AspNetCore.Identity.UI.Services;
using SendGrid;
using SendGrid.Helpers.Mail;

namespace WebAppIdentity;

public class SendGridEmailSender : IEmailSender
{
    private readonly IConfiguration configuration;
    private readonly ILogger logger;

    public SendGridEmailSender(IConfiguration configuration, ILogger<SendGridEmailSender> logger)
    {
        this.configuration = configuration;
        this.logger = logger;
    }

    public async Task SendEmailAsync(string toEmail, string subject, string message)
    {
        string sendGridApiKey = configuration["SendGridApiKey"];
        if (string.IsNullOrEmpty(sendGridApiKey))
        {
            throw new Exception("The 'SendGridApiKey' is not configured");
        }

        var client = new SendGridClient(sendGridApiKey);
        var msg = new SendGridMessage()
        {
            From = new EmailAddress("[YOUR_EMAIL_ADDRESS]", "[YOUR_WEBSITE_NAME]"),
            Subject = subject,
            PlainTextContent = message,
            HtmlContent = message
        };
        msg.AddTo(new EmailAddress(toEmail));

        var response = await client.SendEmailAsync(msg);
        if (response.IsSuccessStatusCode)
        {
            logger.LogInformation("Email queued successfully");
        }
        else
        {
            logger.LogError("Failed to send email");
            // Adding more information related to the failed email could be helpful in debugging failure,
            // but be careful about logging PII, as it increases the chance of leaking PII
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Replace [YOUR_EMAIL_ADDRESS] with the email address you verified with SendGrid earlier, and replace [YOUR_WEBSITE_NAME] with any name. The name will be displayed to the recipient of the emails, so using the name of your website or service would make sense here.

To send emails with SendGrid, you'll need to get the SendGrid API key from the application's configuration, which is why IConfiguration configuration has been added to the constructor parameters. You'll also want to log whether the emails are sent successfully, which is why the ILogger<SendGridEmailSender> logger parameter has been added to the constructor.

Both of these parameters will be injected by ASP.NET Core's dependency injection container, and then stored as fields of the class.

In SendEmailAsync, the SendGrid API key is retrieved from the application's configuration and used to create a new SendGridClient. Then, a SendGridMessage is created, passing in all the parameters passed into the SendGridMessage method.

That message is then sent using the SendGridClient. If the email has been submitted to SendGrid's API successfully, "Email queued successfully" will be logged, otherwise "Failed to send email" is logged as an error.

The project now expects the 'SendGridApiKey' to be configured, but it isn't yet. You could configure the API key in a variety of ways, but because it is a secret, you should avoid storing it in your source code. That's why .NET introduced the Secret Manager tool to store secrets for local development. Initialize the Secret Manager tool for your project by running the following command:

dotnet user-secrets init
Enter fullscreen mode Exit fullscreen mode

Run the following command to configure 'SendGridApiKey' using the Secret Manager:

dotnet user-secrets set SendGridApiKey [YOUR_SENDGRID_API_KEY]
Enter fullscreen mode Exit fullscreen mode

Replace [YOUR_SENDGRID_API_KEY] with the SendGrid API key you took note of earlier.

With the API key configured, the SendGridEmailSender is ready to be registered. Update the Program.cs file to register the SendGridEmailSender as a service:

using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Identity.UI.Services;
using WebAppIdentity;
using WebAppIdentity.Data;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlite(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();

builder.Services.AddTransient<IEmailSender, SendGridEmailSender>();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseMigrationsEndPoint();
}
else
{
    app.UseExceptionHandler("/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthentication();
app.UseAuthorization();

app.MapRazorPages();

app.Run();
Enter fullscreen mode Exit fullscreen mode

That's it!

Save both files and start your project using the .NET CLI:

dotnet run
Enter fullscreen mode Exit fullscreen mode

Take note of the console logs and you’ll see a line with the format: Now listening on: https://localhost:XXXX. Navigate to your web application with this URL in a web browser, click the Register link, fill out the form, and click Register. The next page will tell you to check your email to confirm your account.

Open your email inbox, open the "Confirm your email" email, and click the link in the email to confirm your email address. By clicking the link, your web application will be opened in a new tab and say "Thank you for confirming your email.".

An email to confirm your email containing a confirmation link.

Great job!

Alternatively, you can send emails using SMTP and SendGrid SMTP relay, as shown in this tutorial.

Stop the project by pressing ctrl + c in your shell.

Add SendGridClient to ASP.NET's Dependency Injection container

SendGrid offers an additional package to easily integrate SendGrid into ASP.NET's dependency injection (DI) container. Instead of creating the SendGridClient manually in the SendGridEmailSender class, with this package, you can let the DI container inject an instance of ISendGridClient into the constructor.

Add the SendGrid dependency injection NuGet package using the following command:

dotnet add package SendGrid.Extensions.DependencyInjection
Enter fullscreen mode Exit fullscreen mode

Next, register SendGrid as a service using the builder.Services.AddSendGrid method, by updating the Program.cs file as below:

using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Identity.UI.Services;
using SendGrid.Extensions.DependencyInjection;
using WebAppIdentity;
using WebAppIdentity.Data;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlite(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();

builder.Services.AddSendGrid(options =>
    options.ApiKey = builder.Configuration.GetValue<string>("SendGridApiKey")
                     ?? throw new Exception("The 'SendGridApiKey' is not configured")
);
builder.Services.AddTransient<IEmailSender, SendGridEmailSender>();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseMigrationsEndPoint();
}
else
{
    app.UseExceptionHandler("/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthentication();
app.UseAuthorization();

app.MapRazorPages();

app.Run();
Enter fullscreen mode Exit fullscreen mode

Now, in the SendGridEmailSender.cs file, you can delete the lines to create the SendGridClient and accept an instance of ISendGridClient via the constructor instead. Here's what the updated file looks like:

using Microsoft.AspNetCore.Identity.UI.Services;
using SendGrid;
using SendGrid.Helpers.Mail;

namespace WebAppIdentity;

public class SendGridEmailSender : IEmailSender
{
    private readonly ISendGridClient sendGridClient;
    private readonly ILogger logger;

    public SendGridEmailSender(ISendGridClient sendGridClient, ILogger<SendGridEmailSender> logger)
    {
        this.sendGridClient = sendGridClient;
        this.logger = logger;
    }

    public async Task SendEmailAsync(string toEmail, string subject, string message)
    {
        var msg = new SendGridMessage()
        {
            From = new EmailAddress("[YOUR_EMAIL_ADDRESS]", "[YOUR_WEBSITE_NAME]"),
            Subject = subject,
            PlainTextContent = message,
            HtmlContent = message
        };
        msg.AddTo(new EmailAddress(toEmail));

        var response = await sendGridClient.SendEmailAsync(msg);
        if (response.IsSuccessStatusCode)
        {
            logger.LogInformation("Email queued successfully");
        }
        else
        {
            logger.LogError("Failed to send email");
            // Adding more information related to the failed email could be helpful in debugging failure,
            // but be careful about logging PII, as it increases the chance of leaking PII
        }
    }
}

Enter fullscreen mode Exit fullscreen mode

When you run the project again, you'll see that everything still works, as before, but in addition to injecting ISendGridClient into the SendGridEmailSender class, you can accept ISendGridClient anywhere in your application that supports dependency injection.

Sending identity emails in ASP.NET Core with SendGrid

ASP.NET Core provides a straightforward interface, IEmailSender, to let developers implement their own email integration. When you implement IEmailSender and register it as a service, ASP.NET Core Identity will start using your implementation to send confirmation and password reset emails. You can quickly implement the IEmailSender using SendGrid's .NET library.

Additional resources

Check out the following resources for more information on the topics and tools presented in this tutorial:

Account confirmation and password recovery in ASP.NET Core – If you want to learn more about the emails for confirming email address and recovering password, check out this Microsoft documentation.

SendGrid C# .NET SDK on GitHub – This GitHub repository contains helpful documentation on how to integrate with SendGrid using .NET using SendGrid's .NET library.

Configure Console app on Microsoft Docs – You used environment variables to store the API Key in this tutorial, but it is recommended to use .NET's configuration APIs. Learn how to configure .NET console applications using the host builder.

Source Code to this tutorial on GitHub – Use this source code if you run into any issues, or submit an issue on this GitHub repo if you run into problems.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .