In my ASP.NET Core 1.1.1 app developed on VS2017 Ver 15.3.3, I'm using Account confirmation and password recovery in ASP.NET Core and MailKit to implement the above article's functionality but I'm getting the following error:
Note:
The error occurs at line await client.ConnectAsync("smtp.relay.uri", 25, SecureSocketOptions.None).ConfigureAwait(false); of SendEmailAsync(...) method below and
at line await _emailSender.SendEmailAsync(model.Email, "Confirm your account", $"Please confirm your account by clicking this link: <a href='{callbackUrl}'>link</a>"); of the Register(...) post method also shown below:
ERROR
SocketException: No such host is known
MessageServices.cs class
public class AuthMessageSender : IEmailSender, ISmsSender
{
public async Task SendEmailAsync(string email, string subject, string message)
{
// Plug in your email service here to send an email.
//return Task.FromResult(0);
var emailMessage = new MimeMessage();
//You can if required (and I have done in my code) set the LocalDomain used when communicating with the SMTP server
//This will be presented as the origin of the emails. In my case I needed to supply the domain so that our internal testing SMTP server would accept and relay my emails.
//We then asynchronously connect to the SMTP server. The ConnectAsync method can take just the uri of the SMTP server or as I’ve done here be overloaded with a port and SSL option. For my case when testing with our local test SMTP server no SSL was required so I specified this explicitly to make it work.
emailMessage.From.Add(new MailboxAddress("MyName", "MyEmail#MyDomain.com"));
emailMessage.To.Add(new MailboxAddress("", email));
emailMessage.Subject = subject;
emailMessage.Body = new TextPart("plain") { Text = message };
using (var client = new SmtpClient())
{
client.LocalDomain = "smtp.MyDomain.com";
await client.ConnectAsync("smtp.relay.uri", 25, SecureSocketOptions.None).ConfigureAwait(false);
await client.SendAsync(emailMessage).ConfigureAwait(false);
await client.DisconnectAsync(true).ConfigureAwait(false);
}
}
Register post method in AccountController.cs
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Register(RegisterViewModel model, string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
// For more information on how to enable account confirmation and password reset please visit https://go.microsoft.com/fwlink/?LinkID=532713
// Send an email with this link
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
var callbackUrl = Url.Action(nameof(ConfirmEmail), "Account", new { userId = user.Id, code = code }, protocol: HttpContext.Request.Scheme);
await _emailSender.SendEmailAsync(model.Email, "Confirm your account",
$"Please confirm your account by clicking this link: <a href='{callbackUrl}'>link</a>");
await _signInManager.SignInAsync(user, isPersistent: false);
_logger.LogInformation(3, "User created a new account with password.");
return RedirectToLocal(returnUrl);
}
AddErrors(result);
}
UPDATE
Not certain, but the error may be related to me not using my email host info correctly:
I'm using a website/email hosting company DiscountASP.NET's webmail feature. Their SMTP Server name for each subscriber is smtp.YourDomainName.com. And hence, in the SendEmailAsync(...) method above, I'm using client.LocalDomain = "smtp.MyDomain.com";
For MailKit implementation I'm following Sending email via a SMTP server section of this article.
Open your cmd and write ipconfig /all for windows and search for your hostname of your PC
then enter your Hostname in this function
using (var client = new SmtpClient())
{
client.LocalDomain = "smtp.MyDomain.com";
await client.ConnectAsync("YourHostName", 25,false);
await client.SendAsync(emailMessage).ConfigureAwait(false);
await client.DisconnectAsync(true).ConfigureAwait(false);
}
Related
I am trying to implement a feature to change the email of a user. Asp net core identity by default allows the user to change an email however it requires a confirmation link. I was wondering if it was possible to not use the confirmation link and just edit the function to update the email of the user with the new email. Thanks in advance
I have tried doing
await _userManager.ChangeEmailAsync(user, Input.NewEmail, code);
and
var changingser = _userManager.Users.First(u => u.Email.Equals(email));
changingser.Email = Input.NewEmail;
Which did not seem to work
Email.cshtml: Change Email function
public async Task<IActionResult> OnPostChangeEmailAsync()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
if (!ModelState.IsValid)
{
await LoadAsync(user);
return Page();
}
var email = await _userManager.GetEmailAsync(user);
if (Input.NewEmail != email)
{
var userId = await _userManager.GetUserIdAsync(user);
var code = await _userManager.GenerateChangeEmailTokenAsync(user, Input.NewEmail);
code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
await _userManager.ChangeEmailAsync(user, Input.NewEmail, code);
/* var callbackUrl = Url.Page(
"/Account/ConfirmEmailChange",
pageHandler: null,
values: new { userId = userId, email = Input.NewEmail, code = code },
protocol: Request.Scheme);*/
//await _emailSender.SendEmailAsync(
// Input.NewEmail,
// "Confirm your email",
// $"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
StatusMessage = "Email has been changed";
return RedirectToPage();
}
StatusMessage = "Your email is unchanged.";
return RedirectToPage();
}
Edit: Failed invalid token?
Yes, you can.
Use SetEmailAsync to see the user email.
This will still require the email to be 'confirmed' so generate a token, then immediately confirm the new email address.
Something like:
await _userManager.SetEmailAsync(user, Input.NewEmail);
var token = await _userManager.GenerateEmailConfirmationTokenAsync(existingUser);
await _userManager.ConfirmEmailAsync(existingUser, token);
I recently added SendGrid to my ASP.net Core 2.2 MVC project. In order to make the automated account confirmation and password reset emails more professional I've tried to implement the dynamic templates. Email was working fine before, but as soon as I added the Template ID using the SendGrid helper, the emails won't send. When I remove the template ID, everything works fine again. I've sent the JSON snippet (i think that's what it's called?) to SendGrid support and they said it runs fine on their end, so something is stopping it from executing in the program. I've tried adding and removing subject and content in case it didn't like that, but that didn't change anything. My code is below. Thank you so much for any help or ideas to try.
public interface IEmailSender
{
Task SendEmailAsync(string email, string subject, string message);
}
public Task SendEmailAsync(string email, string subject, string message)
{
return Execute(Options.SendGridKey, email);
}
public Task Execute(string apiKey, string email)
{
var client = new SendGridClient(apiKey);
var msg = new SendGridMessage()
{
From = new EmailAddress("FROM EMAIL ADDRESS", "SENDER NAME"),
Subject = "testtest",
PlainTextContent = "test1",
HtmlContent = "test2"
};
msg.AddTo(new EmailAddress(email));
// removing the msg.SetTemplateId line allows email to start working again. Adding it back in breaks it.
msg.SetTemplateId("MY TEMPLATE ID");
// Disable click tracking.
// See https://sendgrid.com/docs/User_Guide/Settings/tracking.html
msg.SetClickTracking(false, false);
var debug = (msg.Serialize());
return client.SendEmailAsync(msg);
}
public async Task<IActionResult> Register(RegisterViewModel model, string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
// For more information on how to enable account confirmation and password reset please visit https://go.microsoft.com/fwlink/?LinkID=532713
// Send an email with this link
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
var callbackUrl = Url.Action(nameof(ConfirmEmail), "Account", new { userId = user.Id, code = code }, protocol: HttpContext.Request.Scheme);
await _emailSender.SendEmailAsync(model.Email, "EMAIL SUBJECT",
$"Please confirm your account by clicking this link: <a href='{callbackUrl}'>link</a>");
await _signInManager.SignInAsync(user, isPersistent: false);
_logger.LogInformation(3, "User created a new account with password.");
return RedirectToLocal(returnUrl);
}
AddErrors(result);
}
// If we got this far, something failed, redisplay form
return View(model);
}
Email was working fine before, but as soon as I added the Template ID using the SendGrid helper, the emails won't send. When I remove the template ID, everything works fine again.
I did a test using following code snippet with my SendGrid email service and an existing dynamic template, which work well for me.
You can test it with your apiKey and templateID, and please make sure the templateID you specified is valid.
var client = new SendGridClient(apiKey);
var msg = new SendGridMessage();
msg.SetFrom(new EmailAddress("{email_address_here}", "SENDER NAME"));
msg.AddTo(new EmailAddress("{email_address_here}", "RECEIVER NAME"));
msg.SetTemplateId("{templateID_here}"); // templateID look like: d-4f0a1515501a4a9dad5646a8c1xxxxx
var response = await client.SendEmailAsync(msg);
Note: in above test, install and use Sendgrid v9.12.7.
I am using Arvixe as my web host and have created a .NET 4.5 MVC app with individual user accounts. I created an ADO.NET Entity Data Model database first named "SecurityModel", updated my ApplicationDbContext to "joerdie_securityEntities", and switched my connection string to the following:
<add name="joerdie_securityEntities" connectionString="Data Source=localhost;Initial Catalog=joerdie_security;Integrated Security=false;User ID=myID;Password=myPassword" providerName="System.Data.SqlClient" />
I did this so that I could use my own SQL server db also hosted with Arvixe. When I deploy this base application, I am able to create a new user without an issue, and new users are entered into the SQL server database. However, when I try to create a new user in localhost the user creation fails in the following method during the declaration of "result".
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
try
{
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
// For more information on how to enable account confirmation and password reset please visit https://go.microsoft.com/fwlink/?LinkID=320771
// Send an email with this link
// string code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
// var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
// await UserManager.SendEmailAsync(user.Id, "Confirm your account", "Please confirm your account by clicking here");
return RedirectToAction("Index", "Home");
}
AddErrors(result);
}
catch (Exception e) {
Console.WriteLine(e.Message);
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
An Exception is caught but is null, and I never make it to the following if statement. I am able to bring in other models to this exact SQL Server instance and they update just fine. How do I set this up to be able to debug on my localhost pointing to my SQL Server instance?
Edit: Adding Console output.
Exception thrown: 'System.Data.SqlClient.SqlException' in mscorlib.dll
Application Insights Telemetry (unconfigured): {"name":"Microsoft.ApplicationInsights.Dev.Request","time":"2018-03-24T02:31:06.2230673Z","tags":{"ai.operation.name":"POST Account/Register","ai.operation.id":"MJK7rNb0X7k=","ai.location.ip":"::1","ai.cloud.roleInstance":"joerdie-Desktop","ai.internal.sdkVersion":"web:2.2.0-738"},"data":{"baseType":"RequestData","baseData":{"ver":2,"id":"MJK7rNb0X7k=","name":"POST Account/Register","duration":"00:01:01.1920000","success":true,"responseCode":"200","url":"http://localhost:53015/Account/Register","properties":{"DeveloperMode":"true"}}}}
The thread 0x10ec has exited with code 0 (0x0).
I have two websites using one database, I use asp.net identity (2.2.1.40403) and I have a problem I can't understand. Now, this is a third time this happened and I have no idea where the problem can be.
I have a register and send email method like this
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new User { UserName = model.Email, Email = model.Email, RegisterDate = DateTime.Now };
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
//await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
// For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=320771
await SendConfirmationEmail(user);
return View("ConfirmationEmailSent");
}
AddErrors(result);
}
// If we got this far, something failed, redisplay form
return View(model);
}
private async Task SendConfirmationEmail(Dal.Models.User user)
{
// Send an email with this link
string code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
await UserManager.SendEmailAsync(user.Id, "Potvrzení Vašeho účtu", "Prosím potvrďte svou emailovou adresu kliknutím zde.");
}
What happened is that when user registered he received URL when userId was set to 3d847c51-7217-49fe-ae9d-d8e46e291559, but in database the user was created with 95789d6e-b66e-4c9e-8ee4-fe384b82e838. I don't understand how this can happen. By the way there is no user in database with Id 3d847c51-7217-49fe-ae9d-d8e46e291559. Do you have any idea why and how this can happen?
I would suggest calling back the user by an identifier after create was successful to make sure the properties match up.
//...other code removed for brevity
var user = new User { UserName = model.Email, Email = model.Email, RegisterDate = DateTime.Now };
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
//pick one
//user = await UserManager.FindById(user.Id);
//user = await UserManager.FindByName(user.UserName);
user = await UserManager.FindByEmailAsync(user.Email);
// For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=320771
await SendConfirmationEmail(user);
return View("ConfirmationEmailSent");
}
AddErrors(result);
//...other code removed for brevity
I am also suspect that issue is related to UserManager.CreateAsync() method. You are using correctly. I will rather use manually generated user id instead generated by UserManager.
In your case will be:
var user = new User { UserName = model.Email, Email = model.Email, RegisterDate = DateTime.Now };
user.Id = Guid.NewGuid().ToString();
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await SendConfirmationEmail(user);
return View("ConfirmationEmailSent");
}
I've been learning MVC and got to this part:
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.UserName, Email = model.Email };
UserManager.Delete(user);
//myDBLoginDetails ent = new myDBLoginDetails();
//ent.uspInsertUser(user.UserName, model.Password, model.FirstName, model.LastName, user.Email);
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
// For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=320771
// Send an email with this link
// string code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
// var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
// await UserManager.SendEmailAsync(user.Id, "Confirm your account", "Please confirm your account by clicking here");
return RedirectToAction("Index", "Home");
}
AddErrors(result);
}
// If we got this far, something failed, redisplay form
return View(model);
}
I thought everything was going smoothly and I successfully registered\saved users to the SQL Server db (no MDF file). To continue testing and such I remmed out the code to register\insert the users and deleted the table and recreated it so I could eventually start from scratch.
But then when I tried to re-register a user so I could step through the code to learn what all is going on I discovered my user(s) are still on my PC as I got the messages:
Name tempUser is already taken.
Email 'tempUser#email.com' is already taken.
I tried to delete the user (using UserManager.Delete(user);) but got the message:
[InvalidOperationException: The object cannot be deleted because it
was not found in the ObjectStateManager.]
I searched for an explanation and resolution but have failed, and only discovered there may be other\better ways to register\edit\delete users and frustration eventually set in.
So, I guess I need a couple things solved. I have a LoginDetalsEDmodel.edmx so am I still correct to use a Stored Proc or do I use the Framework? If the Framework then how do I do that?
And.... how do I get rid of the user(s) that are still on my computer (which are gone from the DB)?
I hope I explained this well enough as I'm still learning......