Search code examples
asp.net-coreasp.net-core-mvcsendgrid

.NET MVC Core SendGrid Email Verification not working


Following the Microsoft tutorial, as provided below, however still unable to send the email.

https://learn.microsoft.com/en-us/aspnet/core/security/authentication/accconfirm?view=aspnetcore-2.2&tabs=visual-studio

I have spent some time debugging and found out the issue is due to the following code below getting back null values for the SendGridUser and SendGridKey. Does anyone know how to resolve this issue? Thank you!

Options = optionsAccessor.Value;

Secrets.json

{
  "SendGridUser": "apiKeyName",
  "SendGridKey": "<ApiKey>"
}

enter image description here

EmailSender.cs

using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.Extensions.Options;
using SendGrid;
using SendGrid.Helpers.Mail;
using System.Threading.Tasks;
using ProjectName.Areas.Identity.Services;

namespace ProjectName.Services
{
    public class EmailSender : IEmailSender
    {
        public EmailSender(IOptions<AuthMessageSenderOptions> optionsAccessor)
        {
            Options = optionsAccessor.Value;
        }

        public AuthMessageSenderOptions Options { get; } //set only via Secret Manager

        public Task SendEmailAsync(string email, string subject, string message)
        {
            return Execute(Options.SendGridKey, subject, message, email);
        }

        public Task Execute(string apiKey, string subject, string message, string email)
        {
            var client = new SendGridClient(apiKey);
            var msg = new SendGridMessage()
            {
                From = new EmailAddress("[email protected]", "Joe Smith"),
                Subject = subject,
                PlainTextContent = message,
                HtmlContent = message
            };
            msg.AddTo(new EmailAddress(email));

            // Disable click tracking.
            // See https://sendgrid.com/docs/User_Guide/Settings/tracking.html
            msg.SetClickTracking(false, false);

            return client.SendEmailAsync(msg);
        }
    }
}

AuthMessageSenderOption.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace ProjectName.Areas.Identity.Services
{
    public class AuthMessageSenderOptions
    {
        public string SendGridUser { get; set; }
        public string SendGridKey { get; set; }
    }
}

IdentityHostingStartup.cs

using System;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using ProjectName.Data;

[assembly: HostingStartup(typeof(ProjectName.Areas.Identity.IdentityHostingStartup))]
namespace ProjectName.Areas.Identity
{
    public class IdentityHostingStartup : IHostingStartup
    {
        public void Configure(IWebHostBuilder builder)
        {
            builder.ConfigureServices((context, services) => {

                services.AddDefaultIdentity<IdentityUser>(config =>
                {
                    config.SignIn.RequireConfirmedEmail = true;
                })
                .AddEntityFrameworkStores<ApplicationDbContext>();

            });
        }
    }
}

Configuration from Startup.cs Commented out services.AddIdentity as it was causing conflict with the IdentityHostingStartup

//services.AddIdentity<IdentityUser, IdentityRole>()
//    .AddEntityFrameworkStores<ApplicationDbContext>()
//    .AddDefaultTokenProviders();        

services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
        .AddRazorPagesOptions(options =>
        {
            options.AllowAreas = true;
            options.Conventions.AuthorizeAreaFolder("Identity", "/Account/Manage");
            options.Conventions.AuthorizeAreaPage("Identity", "/Account/Logout");
        });

        services.ConfigureApplicationCookie(options =>
        {
            options.LoginPath = $"/Identity/Account/Login";
            options.LogoutPath = $"/Identity/Account/Logout";
            options.AccessDeniedPath = $"/Identity/Account/AccessDenied";
        });

        // using Microsoft.AspNetCore.Identity.UI.Services;
        //services.AddSingleton<IEmailSender, EmailSender>();

        // requires
        services.AddSingleton<IEmailSender, EmailSender>();
        services.Configure<AuthMessageSenderOptions>(Configuration);

Solution

  • Sorry it was a mistake on my part. Everything is correct.

    The following code below was commented out and replaced to read environment variables from somewhere else and hence the reason the values were null. Thank you very much!

    public Startup(IConfiguration configuration)
           {
               Configuration = configuration;
           }