Hey,
I was following this tutorial: https://docs.microsoft.com/en-us/aspnet/core/security/authentication/accconfirm?tabs=aspnetcore2x%2Csql-server
And email is sent, I'm getting url like this:
_https://localhost:44343/Account/ConfirmEmail?userId=e714dc1d-1b6b-4dfd-8288-78f8b429d506&code=CfDJ8GPqscMYofFCs%2FRH3ucxQlVR4O2ONj8uPQyVUpllt5vd9QrjduDA46u%2FAI9jDaBK28e5U0uzFfx1zxwKQ2O0c41qpXlkVzya%2FU5D1aeLIiMUsiDTxUqil04U%2FRFs%2B%2FZ3UBXmr62zHd%2FxJdsWyo3R4gU1TMwo82OAc0qWrIzG%2BmkxE4SS%2BR0%2BvVVzDyZxLQm4Fc6B9%2Byep6o7ZzVyvhbVKlraFkE6irltJLhKcNFlT6pgmyObNk8uPANP9MXiOdNRvg%3D%3D_
But if I click on it, it does nothing, just redirect to Home/Index and I still can't login, because email is not confirmed.
@HaoK @blowdart
@kironet Do you want to log this in the identity repo, or would you rather we cut and pasted it and closed this one?
@blowdart Is this bug in ASP or my VS just somehow generating project with bug? If 1st one, just fix it :) Thanks
It's never as easy as "Just Fix It". Moved to https://github.com/aspnet/Identity/issues/1390
@blowdart I meant it like, put it on To Do List. Thanks :)
for what it is worth I just tested to make sure email confirmation works in my own app after updating to 2.0, and it does work correctly though there are some differences in the way I am doing things vs the linked tutorial.
When I look at the url provided by @kironet it looks like it would be valid structure if it is in an html message body but if it were in a plain text message body it would not work because the &code= is being encoded for html as
&code=
So if the link is clicked in the context of a plain text message I would expect it to fail. In any case it does not appear to me to a bug in identity but perhaps an issue in how the message body is generated and whether the message body is html or plain text.
I'll try it myself this morning, maybe this can be addressed by updating the docs (cc @Rick-Anderson)
@joeaudette can you suggest the fix to make it work with plain text?
@Rick-Anderson
I can explain what I am doing differently and highlight a gap in the tutorial. In the tutorial it shows this code:
var callbackUrl = Url.EmailConfirmationLink(user.Id, code, Request.Scheme);
await _emailSender.SendEmailConfirmationAsync(model.Email, callbackUrl);
I'm not sure what that Url.EmailConfirmationLink does, maybe it is html encoding?
Also the method SendEmailConfirmationAsync appears like it should be part of IEmailSender but the shown implementation has no such method. The code that is shown in the IEmailSender implementation appears to use the same message for both html and plain text.
In my project I am generating the url just using Url.Action like this:
var callbackUrl = Url.Action(new UrlActionContext
{
Action = "ConfirmEmail",
Controller = "Account",
Values = new { userId = result.User.Id.ToString(), code = result.EmailConfirmationToken },
Protocol = HttpContext.Request.Scheme
});
My own implementation of the method to generate and send the message looks like this:
public async Task SendAccountConfirmationEmailAsync(
ISiteContext siteSettings,
string toAddress,
string subject,
string confirmationUrl)
{
var smtpOptions = await GetSmptOptions().ConfigureAwait(false);
if (smtpOptions == null)
{
var logMessage = $"failed to send account confirmation email because smtp settings are not populated for site {siteSettings.SiteName}";
log.LogError(logMessage);
return;
}
var sender = new EmailSender();
try
{
var plainTextMessage
= await viewRenderer.RenderViewAsString<string>("EmailTemplates/ConfirmAccountTextEmail", confirmationUrl).ConfigureAwait(false);
var htmlMessage
= await viewRenderer.RenderViewAsString<string>("EmailTemplates/ConfirmAccountHtmlEmail", confirmationUrl).ConfigureAwait(false);
await sender.SendEmailAsync(
smtpOptions,
toAddress,
smtpOptions.DefaultEmailFromAddress,
subject,
plainTextMessage,
htmlMessage).ConfigureAwait(false);
}
catch (Exception ex)
{
log.LogError("error sending account confirmation email", ex);
}
}
I am actually using Razor to generate both html and plain text versions of the message. Since the model in this case is just a string with the callback url I'm making sure it is not html encoded in the view for plain text like this:
@model string
@{
Layout = "_LayoutTextEmailNotification";
ViewData["Title"] = "Email Confirmation Required";
}
Please confirm your account by clicking this link: @Html.Raw(Model)
but in the html version I don't use Html.Raw since it should be encoded in that one
looks like possibly this issue also impacts the 2.0 project templates if not a bug in identity, see this question on stackoverflow https://stackoverflow.com/questions/45898502/asp-net-core-confirmemail-not-working
public static Task SendEmailConfirmationAsync(this IEmailSender emailSender, string email, string link)
{
return emailSender.SendEmailAsync(email, "Confirm your email",
$"Please confirm your account by clicking this link: <a href='{HtmlEncoder.Default.Encode(link)}'>link</a>");
}
The problem is here
HtmlEncoder.Default.Encode(link)
just remove it and it works
<a href='{link}'>link</a>
@jones02071987 yeah, thanks I've figured it out already, but I still think it should be fixed. :)
security/authentication/accconfirm https://github.com/aspnet/Docs/issues/4075
@kironet @joeaudette Do we need to open a template bug with the fix as posted by @jones02071987 ?
@Rick-Anderson this is a very nuanced issue because there is no real implementation of IEmailSender in the project template. There is an extension method of IEmailSender that generates the string message for confirmation and that extension is producing an html fragment so it is correct for it to encode the value, therefore it is not a bug per se.
The problem results if someone implements IEmailSender and sends a plain text email instead of an html email that is what the string is intended for. If someone implements IEmailSender and sends an html email it should work as expected.
I think what really should be changed is IEmailSender, specifically the "message" parameter in the signature for SendEmailAsync should probably be renamed to "htmlMessage" or maybe even have 2 parameters htmlMessage and plainTextMessage in that method signature and then pass in both from the accountcontroller. That way the implementor of IEmailSender has more clarity about what the implementation should do and what kind of string they are dealing with.
I have logged an issue in the templates repo https://github.com/aspnet/Templates/issues/868
To get this to work in all devices. Change the template link from:
… by clicking this link: <a href='{HtmlEncoder.Default.Encode(link)}'>link</a>"); to simply:
… by clicking this link: {link}";
this solved a mobile phone email issue...
The solution to this problem is setting the body as "html" and not "plain" because the email content is HTML and the callback URL generates HTML content which is sent as a plain email to the user.
If you set the content as HTML the '&' will be converted to '&' and the email will display the correct link.
Most helpful comment
The problem is here
just remove it and it works