AspNet Core Generate and Change Email Address - asp.net-mvc

I am trying to implement a way for users to change their email in AspNetCore so on the Account Management screen I have the change function that will call GenerateChangeEmailTokenAsync on the user manager, then sends the email with the link containing the Token and UserId.
My problem is how do I allow the link the change the email address to the new address since ChangeEmailAsync requires the new email address be entered.
What is the best practice way of implementing this functionality? I do not want to send over the new email address in the email link but also do not want to make them type the email again. Hopefully someone has done this, I cannot find it anywhere only, and it should be very simple.

I know it is late answering this, but I was looking for it myself before, and thought I leave the answer here for others.
The GenerateChangeEmailTokenAsync method takes the new email as part in the hash of the token.
Next you create a link that contains the token, the new email and the old email
var token = await _userManager.GenerateChangeEmailTokenAsync(user, model.NewEmail);
var resetLink = Url.Action("ChangeEmailToken", "account", new {token = token, oldEmail = user.Email, newEmail = model.newEmail }, protocol: HttpContext.Request.Scheme);
Next you send this link to the user in an email.
When clicked, the user hits the method named in the link (here "ChangeEmailToken" on AccountController:
[AllowAnonymous]
[HttpGet]
public async Task<IActionResult> ChangeEmailToken([FromQuery] string token, [FromQuery] string oldEmail, [FromQuery] string newEmail)
Next you need to verify the token, and -if succesful- update the email address.
var result = await _userManager.ChangeEmailAsync(user, newEmail, token);

The normal flow is to let the user update profile as usual.
If the user updated their email address then that new email address needs to be verified.
That is when you generate the token with GenerateChangeEmailTokenAsync.
You send that token as a link to the new email address.
When the user clicks the link in the new email it takes them back to you site which automatically confirms the token and verifies the new email address.

Related

Force a user to re-enter credentials before submit

Using MVC5, i have an application which a user must be logged into, and then can perform standard actions on some data (create, edit, delete).
I would like to add a credentials prompt, whenever a certain task if performed. So say for example a user is editing a row of data. I want them to be prompted to enter their login credentials again when they hit the Save button, before the row is updated. To be clear, they are ALREADY logged in, i just want to force them to re-confirm their credentials before being allowed to save.
How can i do this in the controller? I want a seperate screen/popup to show, asking for username and password (which will then be checked to ensure correct user credentials) before allowing update of the data.
I looked at creating a new method in the controller, which is passed a username and password, which looks after checking the users credentials again. But how do I go about calling this from the Edit screen, when I also need a popup to appear? Do i go down the route of adding a hidden div on the Edit view, which shows when the user clicks the Save button, and it then calls the method?
Generally, you're expected to attempt a solution, first. Then, if you run into specific issues, you can ask a question about those specific issues. I will tell you that this should be relatively straight-forward. All you need is for the user to re-enter their password. Just add a password input to your edit form and bind it to something on your view model, or you can simply bind it directly to an action parameter, in addition to your view model:
[HttpPost]
public ActionResult MyAction(MyViewModel model, string password)
If you want it to be done in a popup, simply include the popup HTML within the form (so the that the input in the popup will be part of the form) or you'll need to use JavaScript to set another input within the form, which would be bound to either a view model property or action param. Either way, the point is that the password should be posted along with the rest of the form data.
Once inside your post action, you can verify the password by manually:
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
var verifyPassword = UserManager.PasswordHasher.VerifyHashedPassword(user.PasswordHash, password);
if (verifyPassword == PasswordVerificationResult.Failed)
{
ModelState.AddModelError("", "Password incorrect.");
// If password is incorrect, ModelState will be invalid now
}
if (ModelState.IsValid)
{
// save posted data
}
It sounds like you'd ideally want an action which you can call asynchronously from the client. While this can take the form of a standard MVC controller action, you may want to consider building this into a Web API controller (Generally we would use Web API controllers to serve up non-HTML responses). You can read more about Web API in many places on the web so I won't go into that now, but let's say you have a HttpResponseMessage result method which looks like this:
[HttpPost]
public HttpResponseMessage CheckCredentials(string username, string password)
{
// Check user credentials and return either one of the following results:
// If credentials valid
return Request.CreateResponse(HttpStatusCode.OK);
// If not valid
return Request.CreateErrorResponse(HttpStatusCode.BadRequest);
}
Using this pattern you could return a '200 OK' response for valid credentials and a '400 Bad Request' for invalid credentials.
As you already stated, you could have the HTML content required for authentication prompt hidden on the page. When the user performs an action which requires authentication, you could render the popup. When the user submits the popup you could fire off an asynchronous request to the Web API endpoint which you created earlier. Depending on the response you get back, you either proceed with the task or prompt for credentials again with an error message.
Obviously as you'd be sending user credentials over a we request, make sure you're making use of HTTPS.
EDIT:
As Chris mentioned below, this solution leaves your 'quick check' in the hands of the client. While this is fine when you simply want to provide a way to stop the user from easily carrying out an action without re-entering their credentials, you should not rely entirely on it.
You could store the username and password as hidden fields and include them with your main synchronous POST. This would allow you to check that the user entered valid credentials from the server.

OWIN social facebook provider will not list public profile

I making a facebook login using the new setup in ASPNET mvc 5. When the user login I can see that there is a request for the default public profile, but all I'm getting back is the token, username and nothing else.
I thought that by default I would also get information like first and lastname. And if added to the scope also the email.
On the callback from facebook I'm having this code to extract the information:
var authManager = HttpContext.GetOwinContext().Authentication;
var loginInfo = await authManager.GetExternalLoginInfoAsync();
Thanks to the comment, I was able to find out that I needed to look into the identity of the callback to get the requested information
var loginIdentity = await authManager.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie);
This will provide everything beside the logintoken/provider.
I needed to look at the ExternalIdentity and not the ExternalLoginInfo. The post is updated with this info. Also important to add email to the scope

How does one prevent duplicate email addresses while users modify their email addresses after registration using the default MVC Membership Provider?

I am using the default ASP.NET MVC Membership Provider and I would like to allow user's to modify their email after they have created their account. I do not want users to be able to use a duplicate email.
How do I allow a user to modify their email and check that the email is not in use elsewhere in the database? I am not sure of the best way to do this type of check when using the default Membership Provider.
note - I know the Membership Provider itself performs this check when a user attempts to register their email address, I do not know how to perform this check at a later time (due to noobness).
note 2 - I only know of this method of accessing the user's email, is this the proper way to be accessing it?
MembershipUser useremail = Membership.GetUser(User.Identity.Name);
ViewBag.Email = useremail.Email;
You can search for an existing username by that email:
String userName = MembershipProvider.GetUserNameByEmail(email)
If no match is found, userName will be null. See here for more info on this.
Also, if your MembershipProvider has RequiresUniqueEmail = true then this check should already be performed for you - as per this page.

How to use email as httpcontext.User.Identity.name

I'm creating a new asp.net MVC3 app in which in user table I do not wish to have user name but only user id (long) and email (string).
What I would like to understand is normally when I use membership provider with normal db structure, I get the username value in db as the User.Identity.Name.
Could someone please tell me what changes I need to do as I will not have any username in my db to ensure that my email address becomes my User.Identity.Name
Thanks
Arnab
Answer: found out... its FormsAuthentication.SetAuthCookie that decides which value goes to user.identity.name
The value User.Identity.Name typically comes from the "username" you provide the login form. This is stored by the LogOnModel class within the AccountModels model.
To set your email address as User.Identity.Name, you must provide your email address in the username field of the login form. Then use your membership provider to authenticate the user based on the provided email address.
found out... its FormsAuthentication.SetAuthCookie that decides which value goes to user.identity.name

Sharing data between Actions and application behavior

This is my application's current workflow for user registration.
register form ( GET /register )
submit the form ( POST /register )
after success, redirect route to "/registerSuccess"
in (GET /registerSuccess ) view, I want to show the message like "username has been registered successfully".
Controller Actions:
public ActionResult Register()
{
return View();
}
[HttpPost]
public ActionResult Register(string name, string password, string confirmPassword)
{
TempData["registered_user"] = name;
return RedirectToRoute("RegisterSuccess");
}
public ActionResult RegisterSuccess()
{
return View();
}
RegisterSuccess View
<h2><%: TempData["registered_user"] %> has been registered successfully.</h2>
It is working fine except the username is empty when user refresh the page (GET /registerSuccess).
Is it normal behavior in other applications? or Should I do something about it? :) (It is my pet project and requirements come from me :P )
update: Actually, there is one more step which is registered user required admin's approval. So, I can't let user log in after successful register. I'm currently using cookie-based FormsAuthentication to track logged in user.
I prefer to use PRG pattern rather than showing flash message in the submit form.
that is normal behaviour.. when the user 'refreshes' the page is requested directly not through your Register function so registered_user key is gone by then. When you do the initial registration you should use a session marker (say a cookie) to track a successfully logged in user.
Using cookies tutorial is a good place to start
If you were to use HTTP authentication (say Basic Auth) then the browser submits the username and password (in clear text for basic auth) with every request that sends out a 401 Unauthorized (see HTTP/1.0 Protocol)
Hmm...
Normally you should think of a SSO solution, that holds your User's authentication (token).
then you would just need to display the current authenticated user (as a completed registration normally logs in the user)
other solution would be, to add the user in the url, so that the success page has all parameters.
/registerSuccess/<username>
(and maybe check, that the logged in user is just registered, and is the same)
for your own fun project, i would do the second one. ist just plain simple.

Resources