Search code examples
c#asp.net-coreasp.net-identityrazor-pages

'Forgot Email' function not sending email


I'm building my first ASP.NET Core Razor Pages application.

I have created an EmailSender class, which implements IEmailSender.

public class EmailSender : IEmailSender
{
    private readonly EmailSettings Settings;

    public EmailSender(IOptions<EmailSettings> emailSettings)
    {
        Settings = emailSettings.Value;
    }

    public async Task SendEmailAsync(string email, string subject, string htmlMessage)
    {
        using (MailMessage message = new MailMessage())
        using (SmtpClient client = new SmtpClient())
        {
            client.Host = Settings.Host;
            client.Port = Settings.Port;
            client.Credentials = new NetworkCredential(Settings.UserName, Settings.Password);
            client.EnableSsl = Settings.EnableSsl;

            message.To.Add(email);
            message.From = new MailAddress(Settings.FromAddress);
            message.Subject = subject;
            message.Body = htmlMessage;
            message.IsBodyHtml = true;

            await client.SendMailAsync(message);
        }
        await Task.CompletedTask;
    }
}

And I've registered EmailSender in Startup.cs.

public void ConfigureServices(IServiceCollection services)
{

    // Default initialization...

    services.AddTransient<IEmailSender, EmailSender>();
    services.Configure<EmailSettings>(Configuration.GetSection("EmailSettings"));
}

My code builds with no errors.

Using breakpoints, I can see that when I run the application and click Forgot your Password, the EmailSender constructor is called and the EmailSettings instance has the expected values. And when I enter my email address and click Reset Password, the EmailSender constructor is called a second time. However, EmailSender.SendEmailAsync() is never called, and no email is sent.

I'm convinced my EmailSender class is setup right because the constructor is being called. But I don't know how to isolate the reason SendEmailAsync() is never called.

Does anyone have a suggestion?


Solution

  • Scaffold Identity

    You need to scaffold Identity in order to debug the code.

    Right click on the Identity folder and then click

    • Add >> New scaffolded item..

    In the left menu click

    • Identity >> Add

    Then

    • Check Account\ForgotPassword
    • Choose your DB Context class
    • Click add

    Now you have your razor page with code behind so click on the arrow to expand the code behind file.

    In the PostAsync() put a breakpoint on the if statement.

    if (user == null || !(await _userManager.IsEmailConfirmedAsync(user))) // Breakpoint here
    {
        ...
    }
    

    Run the application and do a Forgot Password request.

    Your breakpoint will hit and you will be able to debug. It will not exit the if-statement because await _userManager.IsEmailConfirmedAsync(user) is false.

    Solve that problem by dragging the debugger outside of that loop, and let it continue. Now you will see that your Email sender will be called and your SendEmailAsync will hit.

    To sum it up

    • The 'magic' login, logout and account pages are provided to you by the Identity Team through a library package.
    • In order to access the code you need to scaffold them.
    • You weren't hitting the SendEmailAsync because the user you tried to request a password for was either non-existent or most likely, your user's email wasn't confirmed!