Why my email confirmation token is invalid? - c#

I'm trying to get clean emailconfirmation on Microsoft Identity 2.0. I'm generating email link for confirmation and it's not working.
I found, that encoded token on callbackrul is changing to lowercase and I believe that there is a problem. But I don't know how to solve it.....
When code variable gets value it looks like:
CfDJ8C5jg3XNISFEoFA015AdmTmmbOmQG/cxNBfdOALg4WM+iU6uf8WeJ13buFIzbdlP3tK3kWU1Q6BwGh/DY7dAYqbj+zz7jXGOK1Y79mtDT2jiEKV4NdaLbfaruzOvoKSVhvg4EFVYPAQRrjcAnOkfCxlff6hMVhIBWHkg8rqv7GBnqXvJ+UbCErqlobIxI69YreNWVTM1Z4lkAYQM2xmwRA//0T53KNPTWIX52oDc52eFLJAlsWhXI9uHwgfwTt0X9g==
After callback it's changed to:
cfdj8c5jg3xnisfeofa015admtmmbomqg%2Fcxnbfdoalg4wm%2Biu6uf8wej13bufizbdlp3tk3kwu1q6bwgh%2Fdy7dayqbj%2Bzz7jxgok1y79mtdt2jiekv4ndalbfaruzovoksvhvg4efvypaqrrjcanokfcxlff6hmvhibwhkg8rqv7gbnqxvj%2Bubcerqlobixi69yrenwvtm1z4lkayqm2xmwra%2F%2F0t53knptwix52odc52efljalswhxi9uhwgfwtt0x9g%3D%3D
On confirmation action I'm getting decoded token as:
cfdj8c5jg3xnisfeofa015admtmmbomqg/cxnbfdoalg4wm+iu6uf8wej13bufizbdlp3tk3kwu1q6bwgh/dy7dayqbj+zz7jxgok1y79mtdt2jiekv4ndalbfaruzovoksvhvg4efvypaqrrjcanokfcxlff6hmvhibwhkg8rqv7gbnqxvj+ubcerqlobixi69yrenwvtm1z4lkayqm2xmwra//0t53knptwix52odc52efljalswhxi9uhwgfwtt0x9g==
Part of register.cshtml.cs
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
var callbackUrl = Url.Page(
"/Account/ConfirmEmail",
pageHandler: null,
values: new { userId = user.Id, code = codeenc },
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>.");
confirmemail.cshtml.cs
var user = await _userManager.FindByIdAsync(userId);
if (user == null)
{
return NotFound($"Unable to load user with ID '{userId}'.");
}
var result = await _userManager.ConfirmEmailAsync(user, code);
So where I make a mistake?

I'm not sure but did you try this one? code = System.Web.HttpUtility.UrlEncode(code);

Problem was in my startup.cs file,
options.LowercaseQueryStrings = true;
After comment token is working ok.

Related

Successfully sending email but link is null

I use Url.page() function but it always returning null value. I tried many times but it still return null value. My email is successfully sent but link is null.
var link = Url.Page("/Account/ConfirmEmailModel/",
pageHandler: null,
values: new { area = "Identity", userId = user.Id, code = code, returnUrl = returnUrl },
protocol: Request.Scheme, Request.Host.ToString());
await _emailSender.SendEmailAsync(Input.Email, "Confirm your email", $"verify email.");

Using UserManager to change the email of a user

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);

Asp.net Core 2.2 SendGrid Dynamic Template Integration

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.

_UserManager.CreateAsync() returns sucess but does not save the user on database

I am building a web app in .Net Core 3.0 and everything is running smooth but I found a small issue:
I have a method to create a user in a controller that runs well, saves the user and its all ok.
But I have the need to have another method in another controler that can also create users, and this method is copy / paste from the other working method that works, uses the same UserManager that is IOC'ed, it returns sucess, but at the end the user is not in the database.
This has to be in another method as this one if for creating users for other people, where the password is auto generated. The password passes and does not give any error on the createAsync() and it complies with the settings in the startup.cs file
I have changed the method, tryed to stimulate the EF context, to check if it was something on it, but no....
[HttpPut]
[Authorize(Roles = "Admin")]
[Route("CreateEmployee")]
public async Task<IActionResult> CreateEmployee([FromQuery]string email)
{
var user = new IdentityUser { UserName = email, Email = email };
var pw = UserHelper.GenerateRandomPassword(10);
var userResult = await _UserManager.CreateAsync(user, pw);
var resultRole = await _UserManager.AddToRoleAsync(user, "Employee");
var code = await _UserManager.GenerateEmailConfirmationTokenAsync(user);
var callbackUrl = Url.Page(
"/Account/ConfirmEmail",
pageHandler: null,
values: new { userId = user.Id, code = code },
protocol: Request.Scheme);
EmailSender sender = new EmailSender();
await sender.SendWelcomeEmailAsync("Email Header",
new NewUserData { ConfirmUrl = callbackUrl, Password = pw, Email = email, Username = $"" });
}
Inspecting the object userResult :
userResult .succeded = true
uuserResult er.Errors.Count = 0
Inspecting the object resultRole :
resultRole.succeded = true
resultRole.Errors.Count = 0

MVC 5 ASP.NET Identity - CreateAsync invalid user id

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");
}

Categories