Skip to content

Account confirmation+PW recovery article for BWA/Identity components #31383

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
DwayneSelsig opened this issue Jan 8, 2024 · 6 comments · Fixed by #31431
Closed

Account confirmation+PW recovery article for BWA/Identity components #31383

DwayneSelsig opened this issue Jan 8, 2024 · 6 comments · Fixed by #31431
Assignees
Labels
8.0 .NET 8 Blazor doc-idea Pri1 Source - Docs.ms Docs Customer feedback via GitHub Issue

Comments

@DwayneSelsig
Copy link

Description

Issue Description:
While creating a new Blazor Server web app with individual accounts, as per the tutorial instructions, I noticed the following line is automatically added to the program.cs file:

builder.Services.AddSingleton<IEmailSender<ApplicationUser>, IdentityNoOpEmailSender>();

However, throughout the tutorial, there appears to be no reference or guidance on how to utilize this service. Due to this, the process of sending a confirmation email, which is a crucial part of the tutorial, does not function as intended.

Page URL

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

Content source URL

https://github.com/dotnet/AspNetCore.Docs/blob/main/aspnetcore/security/authentication/accconfirm.md

Document ID

98e7aacb-2a01-d581-9b03-bfef2893163e

Article author

@Rick-Anderson

@dotnet-bot dotnet-bot added ⌚ Not Triaged aspnet-core/prod Source - Docs.ms Docs Customer feedback via GitHub Issue labels Jan 8, 2024
@guardrex
Copy link
Collaborator

guardrex commented Jan 8, 2024

@DwayneSelsig ... Are you able to replace the registration with the EmailSender provided by the article? ...

- builder.Services.AddSingleton<IEmailSender<ApplicationUser>, IdentityNoOpEmailSender>();
+ builder.Services.AddSingleton<IEmailSender<ApplicationUser>, EmailSender>();

@Rick-Anderson ... Are Jeremy/Halter planning anything for this article now that we have Identity Razor components? I don't see any open issues on it if they do have something planned.

@DwayneSelsig
Copy link
Author

@guardrex When I change that line to what you suggested, I'm getting the following error:

The type 'BlazorApp8.Services.EmailSender' cannot be used as type parameter 'TImplementation' in the generic type or method 'ServiceCollectionServiceExtensions.AddSingleton<TService, TImplementation>(IServiceCollection)'. There is no implicit reference conversion from 'BlazorApp8.Services.EmailSender' to 'Microsoft.AspNetCore.Identity.IEmailSender<BlazorApp8.Data.ApplicationUser>'.

More/Other instructions are required to complete the tutorial.


I have uploaded a demo project here: https://github.com/DwayneSelsig/EmailSenderTutorialFailed

This solution was created as a 'Blazor Web App' project with the following settings:

Framework
.NET 8.0 (Long Term Support)

Authentication type
Individual Accounts

Interactive render mode
Server

Interactivity location
Per page/component

☑Include sample pages

@guardrex
Copy link
Collaborator

guardrex commented Jan 8, 2024

I'm not too surprised given the presence of the type parameter in the line added by the project template.

I'll take a closer look first thing tomorrow morning and see what can be done temporarily to unblock you.

I think I'll end up creating a Blazor-specific version of this article for the Blazor Security node of articles.

@guardrex
Copy link
Collaborator

guardrex commented Jan 9, 2024

The general direction would be something like ...

using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
using BlazorSample.Data;
using SendGrid;
using SendGrid.Helpers.Mail;

namespace BlazorSample.Components.Account;

public class EmailSender(IOptions<AuthMessageSenderOptions> optionsAccessor,
    ILogger<EmailSender> logger) : IEmailSender<ApplicationUser>
{
    private readonly ILogger logger = logger;

    public AuthMessageSenderOptions Options { get; } = optionsAccessor.Value;

    public Task SendConfirmationLinkAsync(ApplicationUser user, string email, string confirmationLink) =>
        SendEmailAsync(email, "Confirm your email", $"Please confirm your account by <a href='{confirmationLink}'>clicking here</a>.");

    public Task SendPasswordResetLinkAsync(ApplicationUser user, string email, string resetLink) =>
        SendEmailAsync(email, "Reset your password", $"Please reset your password by <a href='{resetLink}'>clicking here</a>.");

    public Task SendPasswordResetCodeAsync(ApplicationUser user, string email, string resetCode) =>
        SendEmailAsync(email, "Reset your password", $"Please reset your password using the following code: {resetCode}");

    public async Task SendEmailAsync(string toEmail, string subject, string message)
    {
        if (string.IsNullOrEmpty(Options.SendGridKey))
        {
            throw new Exception("Null SendGridKey");
        }
        await Execute(Options.SendGridKey, subject, message, toEmail);
    }

    public async Task Execute(string apiKey, string subject, string message, string toEmail)
    {
        var client = new SendGridClient(apiKey);
        var msg = new SendGridMessage()
        {
            From = new EmailAddress("Joe@contoso.com", "Password Recovery"),
            Subject = subject,
            PlainTextContent = message,
            HtmlContent = message
        };
        msg.AddTo(new EmailAddress(toEmail));

        // Disable click tracking.
        // See https://sendgrid.com/docs/User_Guide/Settings/tracking.html
        msg.SetClickTracking(false, false);
        var response = await client.SendEmailAsync(msg);
        
        if (response.IsSuccessStatusCode)
        {
            logger.LogInformation("Email to {Email} queued successfully!", toEmail);
        }
        else
        {
            logger.LogInformation("Failure Email to {Email}", toEmail);
        }
    }
}

... which is only pseudocode at this point because I can't use SendGrid here. Malwarebytes flagged malware at their website. I don't use websites that MB flags as dangerous.

Note that I changed the logging at the end because static analysis indicates that the code provided is a code smell. I also think it's better to use the standard log message template than interpolated strings.

Based on a code comment in the no-op provider ...

// Remove the "else if (EmailSender is IdentityNoOpEmailSender)" block from RegisterConfirmation.razor after updating with a real implementation.

... and yes ... that can be done in the RegisterConfirmation component.

Later guidance in the article has you set DisplayConfirmAccountLink to true in the RegisterConfirmationModel. There isn't an exact match for that instruction in the RegisterConfirmation component. There's an emailConfirmationLink. Because that's only used to demo some UI in the component, it can be removed completely from the component leaving just the line instructing the user to check their email ...

-@if (emailConfirmationLink is not null)
-{
-    <p>
-        This app does not currently have a real email sender registered, see <a href="https://aka.ms/aspaccountconf">these docs</a> for how to configure a real email sender.
-        Normally this would be emailed: <a href="@emailConfirmationLink">Click here to confirm your account</a>
-    </p>
-}
-else
-{
    <p>Please check your email to confirm your account.</p>
-}

@code {
-    private string? emailConfirmationLink;

    ...
}

I'm not sure what else there would be to do and wire up the rest of the email infrastructure for the Identity components. I agree with you that we need a version of this article for BWAs.

@Rick-Anderson ... I suppose that there isn't a plan by Halter or Jeremy to write up a bit on what was done for this, so I'll schedule it for myself to work on. I'm not sure about sticking with SendGrid tho. I'll hit up Dan and Artak with an alternative service ... one that doesn't offer so much malware 😨 at their website.

@guardrex guardrex changed the title The provided tutorial needs to be updated for Identity in .NET 8. Account confirmation+PW recovery article for BWA/Identity components Jan 9, 2024
@guardrex guardrex self-assigned this Jan 9, 2024
@github-project-automation github-project-automation bot moved this to Triage in Blazor.Docs Jan 9, 2024
@dotnet dotnet deleted a comment from github-actions bot Jan 9, 2024
@guardrex guardrex moved this from Triage to 8.0 in Blazor.Docs Jan 9, 2024
@guardrex
Copy link
Collaborator

guardrex commented Jan 9, 2024

Here's the main security warning, and I think there are two others, probably trackers.

image

@guardrex
Copy link
Collaborator

guardrex commented Jan 11, 2024

I'm getting close to working on this ... tomorrow (Friday) perhaps if I don't get bogged down in something else.

UPDATE (1/11): Cool ... I just started working on this today (Thursday). I'll probably do with a different provider than SendGrid tho due to the little problem ☝️ 😈 that they're having. This will be a reference article, not a tutorial, but I'll have all of the code and guidance to make the basic features work for account confirmation and PW recovery.

@guardrex guardrex moved this from 8.0 to In progress in Blazor.Docs Jan 11, 2024
@github-project-automation github-project-automation bot moved this from In progress to Done in Blazor.Docs Jan 26, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
8.0 .NET 8 Blazor doc-idea Pri1 Source - Docs.ms Docs Customer feedback via GitHub Issue
Projects
Archived in project
Development

Successfully merging a pull request may close this issue.

3 participants