In microsoft Identity 2 there is ability to users can confirm there email addresses I downloaded Identity 2 sample project from here in this project there isn\'t any differ
Maybe its a little late but I hope it may help others.
Add this
var userid = UserManager.FindByEmail(model.Email).Id;
if (!UserManager.IsEmailConfirmed(userid))
{
return View("EmailNotConfirmed");
}
before
var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
The first block of code just checks if the email in the model exists in the database and gets it's id to check if it is not confirmed and if so returns a view to the user wich says so and if it is confirmed just lets the user sign in.
And delete your changes to the result switch like this
switch (result)
{
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl });
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Invalid login attempt.");
return View(model);
}
There is a solution, which may not be the best approach, but it works. First let me try to clarify why your approach did not work.
In one of the comments it is mentioned, the AuthenticationManager uses cookies. In order to update a cookie you need to send it to the client, using another page. That is why TransferRequest is not going to work.
How to handle the emailverification? The strategy I used:
1) On SignInStatus.Success this means that the user is logged in.
2) When email is not confirmed: send an email to the used e-mailaddress. This is safe since the user already signed in. We are just blocking further access until the e-mail is verified. For each time a user tries to login without having validated the email, a new email (with the same link) is sent. This could be limited by keeping track of the number of sent emails.
3) We cannot use LogOff: this is HttpPost and uses a ValidateAntiForgeryToken.
4) Redirect to a page (HttpGet, authorization required) that displays the message that an e-mail has been sent. On entering sign out the user.
5) For other validation errors, redirect to another method to sign out (HttpGet, authorization required). No view needed, redirect to the login page.
In code: update the code in AccountController.Login to:
case SignInStatus.Success:
{
var currentUser = UserManager.FindByNameAsync(model.Email);
if (!await UserManager.IsEmailConfirmedAsync(currentUser.Id))
{
// Send email
var code = await UserManager.GenerateEmailConfirmationTokenAsync(currentUser.Id);
var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = currentUser.Id, code = code}, protocol: Request.Url.Scheme);
await UserManager.SendEmailAsync(currentUser.Id, "Confirm your account", string.Format("Please confirm your account by clicking this link: <a href=\"{0}\">link</a>", callbackUrl));
// Show message
return RedirectToAction("DisplayEmail");
}
// Some validation
if (true)
{
return RedirectToAction("SilentLogOff");
}
return RedirectToLocal(returnUrl);
}
Add methods to AccountController:
// GET: /Account/SilentLogOff
[HttpGet]
[Authorize]
public ActionResult SilentLogOff()
{
// Sign out and redirect to Login
AuthenticationManager.SignOut();
return RedirectToAction("Login");
}
// GET: /Account/DisplayEmail
[HttpGet]
[Authorize]
public ActionResult DisplayEmail()
{
// Sign out and show DisplayEmail view
AuthenticationManager.SignOut();
return View();
}
DisplayEmail.cshtml
@{
ViewBag.Title = "Verify e-mail";
}
<h2>@ViewBag.Title.</h2>
<p class="text-info">
Please check your email and confirm your email address.
</p>
You'll notice that the user cannot reach other pages until email is verified. And we are able to use the features of the SignInManager.
There is one possible problem (that I can think of) with this approach, the user is logged in for the time that the email is sent and the user is being redirected to the DisplayMessage view. This may not be a real problem, but it shows that we are not preventing the user from logging in, only denying further access after logging in by automatically logging out the user.
=== Update ====
Please note that exceptions have to be handled properly. The user is granted access and then access is revoked in this scenario. But in case an exception occurs before signing out and this exception was not catched, the user remains logged in.
An exception can occur when the mailserver is not available or the credentials are empty or invalid.
===============
Require email confirmation
It's a best practice to confirm the email of a new user registration to verify they are not impersonating someone else (that is, they haven't registered with someone else's email). Suppose you had a discussion forum, and you wanted to prevent "yli@example.com" from registering as "nolivetto@contoso.com." Without email confirmation, "nolivetto@contoso.com" could get unwanted email from your app. Suppose the user accidentally registered as "ylo@example.com" and hadn't noticed the misspelling of "yli," they wouldn't be able to use password recovery because the app doesn't have their correct email. Email confirmation provides only limited protection from bots and doesn't provide protection from determined spammers who have many working email aliases they can use to register.
You generally want to prevent new users from posting any data to your web site before they have a confirmed email.
Update ConfigureServices to require a confirmed email:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<ApplicationUser, IdentityRole>(config =>
{
config.SignIn.RequireConfirmedEmail = true;
})
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
// Add application services.
services.AddTransient<IEmailSender, EmailSender>();
services.AddMvc();
services.Configure<AuthMessageSenderOptions>(Configuration);
}
I would let the admin create the user without any password. The email with link should go to the user. The user then is directed to SetPassword page to set new password. This way no one can access the user account unless he confirms and sets the password.
Call CreateAsync without the password
var adminresult = await UserManager.CreateAsync(user);
Redirect admin to new custom view saying something like "Email is sent to user"
@{
ViewBag.Title = "New User created and Email is Sent";
}
<h2>@ViewBag.Title.</h2>
<p class="text-info">
The New User has to follow the instructions to complete the user creation process.
</p>
<p class="text-danger">
Please change this code to register an email service in IdentityConfig to send an email.
</p>
The answer by @INFINITY_18 may cause Object reference not set to an instance of an object
error if the email does not exist in the data store at all. And why not return the Login view with model error in this case, too?
I would suggest the following:
var userid = UserManager.FindByEmail(model.Email)?.Id;
if (string.IsNullOrEmpty(userid) || !UserManager.IsEmailConfirmed(userid)))
{
ModelState.AddModelError("", "Invalid login attempt.");
return View(model);
}
Instead of moving to another page, why not finish this one and redirect to the right action / view:
if (!await UserManager.IsEmailConfirmedAsync(user.Id))
{
return RedirectToAction("ConfirmEmailAddress", new { ReturnUrl = returnUrl });
}
You do need an action (and possibly a view) with the name ConfirmEmailAddress
though.