Этот вопрос относится к проекту core3 / реагирует с внешним поставщиком удостоверений, созданным следующим образом.
dotnet new react --auth Individual --use-local-db --output conf
и измененным для поддержки внешнего поставщика удостоверений. Пакет добавляется
dotnet add package Microsoft.AspNetCore.Authentication.MicrosoftAccount
, а запуск изменяется
services.AddAuthentication()
.AddMicrosoftAccount(options =>
{
options.ClientId = Configuration["Authentication:Microsoft:ClientId"];
options.ClientSecret = Configuration["Authentication:Microsoft:ClientSecret"];
options.CallbackPath = "/signin-microsoft";
})
После следования инструкциям , предоставленным Microsoft , я проверил свою работу, зарегистрировавшись в качестве пользователя. Не было выдано никаких ошибок, но обещанное электронное письмо с подтверждением так и не пришло.
Следуя советам по устранению неполадок в конце инструкций, я установил точку останова в начале метода SendEmailAsyn c моей реализации IEmailSender и повторил упражнение. Точка останова не достигнута.
Если я вручную подтверждаю учетную запись путем обновления базы данных,
- Я могу войти в систему.
- Ссылка Забыли пароль занимает Я перехожу на страницу восстановления пароля и, используя это, попадает в точку останова и успешно отправляет электронное письмо для сброса пароля со ссылкой, которая работает.
Очевидно, что моя реализация IEmailSender работает и правильно зарегистрирована. Это не совсем то же самое, что и пример кода, потому что у меня есть собственный сервер Exchange и я не использовал SendGrid, но он успешно отправил электронное письмо для сброса пароля, и я могу повторить это любое количество раз без заминки.
Несмотря на малую вероятность того, что это как-то является причиной проблемы, вот моя реализация
public class SmtpEmailSender : IEmailSender
{
public SmtpEmailSender(IOptions<SmtpOptions> options)
{
this.smtpOptions = options.Value;
}
private SmtpOptions smtpOptions { get; }
public Task SendEmailAsync(string email, string subject, string htmlMessage)
{
var smtp = new SmtpClient();
if (!smtpOptions.ValidateCertificate)
{
smtp.ServerCertificateValidationCallback = (s, c, h, e) => true;
}
smtp.Connect(smtpOptions.Host, smtpOptions.Port, SecureSocketOptions.Auto);
if (smtpOptions.Authenticate)
{
smtp.Authenticate(smtpOptions.Username, smtpOptions.Password);
}
var message = new MimeMessage()
{
Subject = subject,
Body = new BodyBuilder() { HtmlBody = htmlMessage }.ToMessageBody()
};
message.From.Add(new MailboxAddress(smtpOptions.Sender));
message.To.Add(new MailboxAddress(email));
return smtp.SendAsync(FormatOptions.Default, message).ContinueWith(antecedent =>
{
smtp.Disconnect(true);
smtp.Dispose();
});
}
}
Регистрация в startup.cs
выглядит следующим образом.
services.AddTransient<IEmailSender, SmtpEmailSender>();
services.Configure<SmtpOptions>(Configuration.GetSection("SmtpOptions"));
SmptOptions - это просто перетаскиваемые настройки из appsettings. json и вводится в ctor. Очевидно, что этот аспект работает, или электронные письма для сброса пароля не будут работать.
В регистрации не может быть ничего плохого, потому что приложение перестает выдавать сообщение о необходимости прочитать и следовать инструкциям подтверждения учетной записи, которые я связал.
Чтобы увидеть, была ли проблема вызвана каким-то непреднамеренным побочным эффектом моего кода, я создал инструментальную заглушку IEmailSender
public class DummyEmailSender : IEmailSender
{
private readonly ILogger logger;
public DummyEmailSender(ILogger<DummyEmailSender> logger)
{
this.logger = logger;
}
public Task SendEmailAsync(string email, string subject, string htmlMessage)
{
logger.LogInformation($"SEND EMAIL\r\nemail={email} \r\nsubject={subject}\r\nhtmlMessage={htmlMessage}\r\n{new StackTrace().ToString().Substring(0,500)}");
return Task.CompletedTask;
}
}
Я также обновил регистрацию службы, чтобы соответствовать.
Это самый простой из возможных инструментальных заглушек, и наблюдаемое поведение такое же, оно вызывается при отправке формы забытого пароля и не вызывается при отправке формы подтверждения регистрации.
Кто-нибудь когда-нибудь получал эту ужасную работу? Как?
Непосредственно перед сбоем этот URL https://wone.pdconsec.net/Identity/Account/ExternalLogin?returnUrl=%2Fauthentication%2Flogin&handler=Callback
выглядит следующим образом
![enter image description here](https://i.stack.imgur.com/awPe7.png)
Проверка найденной страницы кнопка «Зарегистрировать» отправляет форму в /Identity/Account/ExternalLogin?returnUrl=%2Fauthentication%2Flogin&handler=Confirmation
Код для этого доступен в репозитории do tnet. После клонирования репо https://github.com/dotnet/aspnetcore.git
я прочитал инструкции по сборке и преуспел в создании предварительного просмотра tnet 5. Затем я запустил clean
, прежде чем переключиться на помеченную ветку release/3.1
, чтобы создать отладочные пакеты для core3.1, но это не удалось, потому что помеченная ветвь привносит в игру версию msbuild, которая немного устарела, и средство, предложенное в сообщении об ошибке. не похоже на работу. Поскольку мое владение PowerShell слабое (сценарий сборки - PowerShell), я вынужден заниматься проверкой кода. Соответствующий код выглядит следующим образом.
public override async Task<IActionResult> OnPostConfirmationAsync(string returnUrl = null)
{
returnUrl = returnUrl ?? Url.Content("~/");
// Get the information about the user from the external login provider
var info = await _signInManager.GetExternalLoginInfoAsync();
if (info == null)
{
ErrorMessage = "Error loading external login information during confirmation.";
return RedirectToPage("./Login", new { ReturnUrl = returnUrl });
}
if (ModelState.IsValid)
{
var user = CreateUser();
await _userStore.SetUserNameAsync(user, Input.Email, CancellationToken.None);
await _emailStore.SetEmailAsync(user, Input.Email, CancellationToken.None);
var result = await _userManager.CreateAsync(user);
if (result.Succeeded)
{
result = await _userManager.AddLoginAsync(user, info);
if (result.Succeeded)
{
_logger.LogInformation("User created an account using {Name} provider.", info.LoginProvider);
var userId = await _userManager.GetUserIdAsync(user);
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
var callbackUrl = Url.Page(
"/Account/ConfirmEmail",
pageHandler: null,
values: new { area = "Identity", userId = userId, code = code },
protocol: Request.Scheme);
await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
$"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
// If account confirmation is required, we need to show the link if we don't have a real email sender
if (_userManager.Options.SignIn.RequireConfirmedAccount)
{
return RedirectToPage("./RegisterConfirmation", new { Email = Input.Email });
}
await _signInManager.SignInAsync(user, isPersistent: false);
return LocalRedirect(returnUrl);
}
}
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
}
ProviderDisplayName = info.ProviderDisplayName;
ReturnUrl = returnUrl;
return Page();
}
Похоже, он должен работать. Что мы знаем?
- Не выдается никаких необработанных ошибок, оно передается в RegisterConfirmation, которая отправляет сообщение об электронной почте, которая никогда не приходит.
CreateUser
вызывается и преуспевает. Мы знаем это, потому что пользователь создан в базе данных. Таким образом, он определенно проходит мимо, что означает, что ModelState
не равно нулю, а .IsValid
истинно. - IEmailSender.SendEmailAsyn c фактически не вызывается, несмотря на приведенный выше код.
- Если
result.Succeeded
истинно, должно появиться сообщение в журнале, говорящее что-то вроде: «Пользователь создал учетную запись с помощью поставщика учетных записей Microsoft» - Перенаправляет на
https://localhost:5001/Identity/Account/RegisterConfirmation?Email=accountname@outlook.com
I ' Я вижу сообщения журнала для большинства вещей. Попытка зарегистрироваться во второй раз после первого прохода создает пользователя, но не может отправить электронное письмо, на консоли и в журнале событий появляется предупреждение о DuplicateUserName. Установив подтверждение непосредственно в базе данных, мы можем войти в систему, а затем в интерактивном режиме удалить учетную запись, и для этих действий отображаются журналы.
Но журналы для подтверждения не отображаются. То, что действительно делает мою голову в том, что она затем перенаправляет на https://localhost:5001/Identity/Account/RegisterConfirmation?Email=accountname@outlook.com
Это безумие. Чтобы попасть туда, userManager.AddLoginAsync()
должен вернуть true, и следующая строка в этом случае - запись в регистратор о создании учетной записи пользователя.
Это не имеет смысла.