-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Description
Background and Motivation
Should this text be configurable eventually?
I'm reminded of the "We will never ask you for this code" story we heard recently 😄
Probably. It's not configurable with Identity UI, but you can more easily override razor pages than these endpoints. And you can scaffold them out. You could parse out the code in your
IEmailSenderand do whatever, but that'd obviously be very fragile.I think in the future we might have a higher-level abstraction with methods like
SendEmailConfirmationAsyncandSendPasswordResetAsyncand aTUserparameter. The default implementation could then just resolve theIEmailSenderand use the existing strings. That's not something we need to do immediately though.
Originally posted by @halter73 in #49981 (comment)
We now also have a customer requesting this functionality in the preview7 blogpost at https://devblogs.microsoft.com/dotnet/asp-net-core-updates-in-dotnet-8-preview-7/comment-page-2/#comment-18682
Proposed API
- // Microsoft.Extensions.Identity.Core.dll
+ // Microsoft.AspNetCore.Identity.dll (the move to the shared framework only assembly allows DIMs
namespace Microsoft.AspNetCore.Identity.UI.Services;
public interface IEmailSender
{
Task SendEmailAsync(string email, string subject, string htmlMessage);
// Used by MapIdentityApi and Identity UI
+ Task SendConfirmationLinkAsync<TUser>(TUser user, string email, string confirmationLink) where TUser : class
+ {
+ return SendEmailAsync(email, "Confirm your email", $"Please confirm your account by <a href='{confirmationLink}'>clicking here</a>.");
+ }
// Used by Identity UI
+ Task SendPasswordResetLinkAsync<TUser>(TUser user, string email, string resetLink) where TUser : class
+ {
+ return SendEmailAsync(email, "Reset your password", $"Please reset your password by <a href='{resetLink}'>clicking here</a>.");
+ }
// Used by MapIdentityApi since it does not provide a UI to enter the new password into. That's left to custom application code.
+ Task SendPasswordResetCodeAsync<TUser>(TUser user, string email, string resetCode) where TUser : class
+ {
+ return SendEmailAsync(email, "Reset your password", $"Reset your password using the following code: {resetCode}");
+ }
}
// Unchanged, but this also moves from Microsoft.Extensions.Identity.Core.dll to Microsoft.AspNetCore.Identity.dll,
// so it can still implement IEmailSender
public sealed class NoOpEmailSender : IEmailSender
{
public Task SendEmailAsync(string email, string subject, string htmlMessage) => Task.CompletedTask;
}Usage Examples
MyEmailSender.cs
using Azure.Identity;
namespace MyNamespace;
public class MyEmailSender : IEmailSender
{
private readonly EmailClient _client;
public MyEmailSender(IConfiguration config)
{
var credential = new ChainedTokenCredential(
new ClientSecretCredential(
_config["AZURE_TENANT_ID"],
_config["AZURE_CLIENT_ID"],
_config["AZURE_CLIENT_SECRET"]),
new ManagedIdentityCredential()
)
_client = new EmailClient(new Uri("https://my-instance.communication.azure.com/")
}
public Task SendEmailAsync(string email, string subject, string message)
{
var recipients = new EmailRecipients(new [] { new EmailAddress(email) });
var content = new EmailContent(subject)
{
PlainText = message
};
await _client.SendAsync(new EmailMessage("[email protected]", content, recipients);
}
public Task SendConfirmationLinkAsync<TUser>(TUser user, string email, string confirmationLink) where TUser : class
{
return SendEmailAsync(email, "Confirm your email for MyWebSite", $"Please confirm your MyWebSite account by <a href='{confirmationLink}'>clicking here</a>.");
}
public Task SendPasswordResetLinkAsync<TUser>(TUser user, string email, string resetLink) where TUser : class
{
return SendEmailAsync(email, "Reset your password for MyWebSite", $"Please reset your MyWebSite password by <a href='{resetLink}'>clicking here</a>.");
}
public Task SendPasswordResetCodeAsync<TUser>(TUser user, string email, string resetCode) where TUser : class
{
return SendEmailAsync(email, "Reset your password for MyWebSite", $"Reset your MyWebSite password using the following code: {resetCode}");
}
}Program.cs
// ...
builder.Services.AddSingleton<IEmailSender, MyEmailSender>();
// ...
Alternative Designs
- We could not add the DIMs and instead force customers who care to parse the
subjectandhtmlMessageto figure out what kind of email is being sent and try to extract any links or password reset codes. - We could omit the
TUser userparameters since it's unneeded by the default implementation, but it seems like it could be useful and it is not difficult to pass in.
Risks
In the future, we might not need to send these exact kinds of emails. Maybe the code in Identity UI or MapIdentityApi will have the need to specify more parameters than just the recipient and email confirmation link. In that case, we'd probably add more DIMs, but it would be less clear which ones are actually used.