asp.net - obtener - .NET Identity Email/Nombre de usuario cambia
obtener el usuario logueado asp net mvc (3)
¿Alguien sabe cómo habilitar a un usuario para cambiar el nombre de usuario / correo electrónico con identidad ASP.NET con la confirmación por correo electrónico? Hay muchos ejemplos sobre cómo cambiar la contraseña, pero no puedo encontrar nada al respecto.
Seguí los pasos de Jonathan en un nuevo proyecto ASP.NET para probar los cambios y funcionó a las mil maravillas. Este es el enlace al repository
Trailmax hizo la mayor parte del trabajo correctamente, pero como lo señalan los comentarios, el usuario estaría esencialmente varado si se equivocaran al actualizar su nueva dirección de correo electrónico.
Para solucionar esto, es necesario agregar propiedades adicionales a su clase de usuario y modificar el inicio de sesión. (Nota: esta respuesta se dirigirá a través de un proyecto MVC 5)
Aquí es donde lo tomé:
1. Modifique su objeto Usuario Primero, actualicemos el Usuario de la Aplicación para agregar el campo adicional que necesitaremos. Agregará esto en el archivo IdentiyModel.cs en su carpeta Modelos:
public class ApplicationUser : IdentityUser
{
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
[MaxLength(256)]
public string UnConfirmedEmail { get; set; }//this is what we add
}
Si desea ver un ejemplo más detallado de lo que se hace, consulte esto aquí http://blog.falafel.com/customize-mvc-5-application-users-using-asp-net-identity-2-0/ (ese es el ejemplo que utilicé)
Además, no lo menciona en el artículo vinculado, pero también querrás actualizar tu tabla AspNetUsers:
ALTER TABLE dbo.AspNetUsers
ADD [UnConfirmedEmail] NVARCHAR(256) NULL;
2. Actualiza tu inicio de sesión
Ahora debemos asegurarnos de que nuestro inicio de sesión también verifique la confirmación del correo electrónico anterior para que las cosas estén "en el limbo" mientras esperamos que el usuario confirme este nuevo correo electrónico:
//
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (!ModelState.IsValid)
{
return View(model);
}
var allowPassOnEmailVerfication = false;
var user = await UserManager.FindByEmailAsync(model.Email);
if (user != null)
{
if (!string.IsNullOrWhiteSpace(user.UnConfirmedEmail))
{
allowPassOnEmailVerfication = true;
}
}
// This now counts login failures towards account lockout
// To enable password failures to trigger account lockout, I changed to shouldLockout: true
var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: true);
switch (result)
{
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return allowPassOnEmailVerfication ? RedirectToLocal(returnUrl) : RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Invalid login attempt.");
return View(model);
}
}
Eso es todo ... ¡esencialmente lo has hecho! Sin embargo, siempre me molestan las respuestas a medias que no te guían más allá de las trampas potenciales que tocarás más adelante, así que continuemos nuestra aventura, ¿o sí?
3. Actualice su Manage / Index
En nuestro index.cshtml, agreguemos una nueva sección para el correo electrónico. Sin embargo, antes de llegar, agreguemos el campo que necesitamos en ManageViewmodel.cs
public class IndexViewModel
{
public bool HasPassword { get; set; }
public IList<UserLoginInfo> Logins { get; set; }
public string PhoneNumber { get; set; }
public bool TwoFactor { get; set; }
public bool BrowserRemembered { get; set; }
public string ConfirmedEmail { get; set; } //add this
public string UnConfirmedEmail { get; set; } //and this
}
Salta a la acción de índice en nuestro controlador Manage para agregar eso a nuestro viewmodel:
var userId = User.Identity.GetUserId();
var currentUser = await UserManager.FindByIdAsync(userId);
var unConfirmedEmail = "";
if (!String.IsNullOrWhiteSpace(currentUser.UnConfirmedEmail))
{
unConfirmedEmail = currentUser.UnConfirmedEmail;
}
var model = new IndexViewModel
{
HasPassword = HasPassword(),
PhoneNumber = await UserManager.GetPhoneNumberAsync(userId),
TwoFactor = await UserManager.GetTwoFactorEnabledAsync(userId),
Logins = await UserManager.GetLoginsAsync(userId),
BrowserRemembered = await AuthenticationManager.TwoFactorBrowserRememberedAsync(userId),
ConfirmedEmail = currentUser.Email,
UnConfirmedEmail = unConfirmedEmail
};
Finalmente, para esta sección, podemos actualizar nuestro índice para permitirnos administrar esta nueva opción de correo electrónico:
<dt>Email:</dt>
<dd>
@Model.ConfirmedEmail
@if (!String.IsNullOrWhiteSpace(Model.UnConfirmedEmail))
{
<em> - Unconfirmed: @Model.UnConfirmedEmail </em> @Html.ActionLink("Cancel", "CancelUnconfirmedEmail",new {email=Model.ConfirmedEmail})
}
else
{
@Html.ActionLink("Change Email", "ChangeEmail")
}
</dd>
4. Agregue esas nuevas modificaciones
Primero, agreguemos ChangeEmail:
Ver modelo:
public class ChangeEmailViewModel
{
public string ConfirmedEmail { get; set; }
[Required]
[EmailAddress]
[Display(Name = "Email")]
[DataType(DataType.EmailAddress)]
public string UnConfirmedEmail { get; set; }
}
Obtener acción:
public ActionResult ChangeEmail()
{
var user = UserManager.FindById(User.Identity.GetUserId());
var model = new ChangeEmailViewModel()
{
ConfirmedEmail = user.Email
};
return View(model);
}
Ver:
@model ProjectName.Models.ChangeEmailViewModel
@{
ViewBag.Title = "Change Email";
}
<h2>@ViewBag.Title.</h2>
@using (Html.BeginForm("ChangeEmail", "Account", FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
{
@Html.AntiForgeryToken()
<h4>New Email Address:</h4>
<hr />
@Html.ValidationSummary("", new { @class = "text-danger" })
@Html.HiddenFor(m=>m.ConfirmedEmail)
<div class="form-group">
@Html.LabelFor(m => m.UnConfirmedEmail, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
@Html.TextBoxFor(m => m.UnConfirmedEmail, new { @class = "form-control" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" class="btn btn-default" value="Email Link" />
</div>
</div>
}
HttpPost Acción:
[HttpPost]
public async Task<ActionResult> ChangeEmail(ChangeEmailViewModel model)
{
if (!ModelState.IsValid)
{
return RedirectToAction("ChangeEmail", "Manage");
}
var user = await UserManager.FindByEmailAsync(model.ConfirmedEmail);
var userId = user.Id;
if (user != null)
{
//doing a quick swap so we can send the appropriate confirmation email
user.UnConfirmedEmail = user.Email;
user.Email = model.UnConfirmedEmail;
user.EmailConfirmed = false;
var result = await UserManager.UpdateAsync(user);
if (result.Succeeded)
{
string callbackUrl =
await SendEmailConfirmationTokenAsync(userId, "Confirm your new email");
var tempUnconfirmed = user.Email;
user.Email = user.UnConfirmedEmail;
user.UnConfirmedEmail = tempUnconfirmed;
result = await UserManager.UpdateAsync(user);
callbackUrl = await SendEmailConfirmationWarningAsync(userId, "You email has been updated to: "+user.UnConfirmedEmail);
}
}
return RedirectToAction("Index","Manage");
}
Ahora agrega esa advertencia:
private async Task<string> SendEmailConfirmationWarningAsync(string userID, string subject)
{
string code = await UserManager.GenerateEmailConfirmationTokenAsync(userID);
var callbackUrl = Url.Action("ConfirmEmail", "Account",
new { userId = userID, code = code }, protocol: Request.Url.Scheme);
await UserManager.SendEmailAsync(userID, subject,
"Please confirm your account by clicking <a href=/"" + callbackUrl + "/">here</a>");
return callbackUrl;
}
Y ahora, finalmente, podemos poner en la cancelación de la nueva dirección de correo electrónico:
public async Task<ActionResult> CancelUnconfirmedEmail(string emailOrUserId)
{
var user = await UserManager.FindByEmailAsync(emailOrUserId);
if (user == null)
{
user = await UserManager.FindByIdAsync(emailOrUserId);
if (user != null)
{
user.UnConfirmedEmail = "";
user.EmailConfirmed = true;
var result = await UserManager.UpdateAsync(user);
}
}
else
{
user.UnConfirmedEmail = "";
user.EmailConfirmed = true;
var result = await UserManager.UpdateAsync(user);
}
return RedirectToAction("Index", "Manage");
}
5. Actualiza ConfirmEmail (el último paso)
Después de todo esto de ida y vuelta ahora podemos confirmar el nuevo correo electrónico, lo que significa que debemos eliminar el correo electrónico anterior al mismo tiempo.
var result = UserManager.ConfirmEmail(userId, code);
if (result.Succeeded)
{
var user = UserManager.FindById(userId);
if (!string.IsNullOrWhiteSpace(user.UnConfirmedEmail))
{
user.Email = user.UnConfirmedEmail;
user.UserName = user.UnConfirmedEmail;
user.UnConfirmedEmail = "";
UserManager.Update(user);
}
}
Actualización de diciembre de 2017 Algunos puntos buenos se han planteado en los comentarios:
- Mejor tener un campo separado para el correo electrónico nuevo mientras se confirma - en los casos en que el usuario ingrese un correo electrónico incorrecto. Espere hasta que se confirme el nuevo correo electrónico, luego conviértalo en el correo electrónico principal. Ver respuesta muy detallada de Chris_ a continuación.
- También podría haber un caso cuando la cuenta con ese correo electrónico ya existe; asegúrese de verificar eso también, de lo contrario puede haber problemas.
Esta es una solución muy básica que no cubre todas las combinaciones posibles, así que usa tu criterio y asegúrate de leer los comentarios. Aquí se han planteado muy buenos puntos.
// get user object from the storage
var user = await userManager.FindByIdAsync(userId);
// change username and email
user.Username = "NewUsername";
user.Email = "[email protected]";
// Persiste the changes
await userManager.UpdateAsync(user);
// generage email confirmation code
var emailConfirmationCode = await userManager.GenerateEmailConfirmationTokenAsync(user.Id);
// generate url for page where you can confirm the email
var callbackurl= "http://example.com/ConfirmEmail";
// append userId and confirmation code as parameters to the url
callbackurl += String.Format("?userId={0}&code={1}", user.Id, HttpUtility.UrlEncode(emailConfirmationCode));
var htmlContent = String.Format(
@"Thank you for updating your email. Please confirm the email by clicking this link:
<br><a href=''{0}''>Confirm new email</a>",
callbackurl);
// send email to the user with the confirmation link
await userManager.SendEmailAsync(user.Id, subject: "Email confirmation", body: htmlContent);
// then this is the action to confirm the email on the user
// link in the email should be pointing here
public async Task<ActionResult> ConfirmEmail(string userId, string code)
{
var confirmResult = await userManager.ConfirmEmailAsync(userId, code);
return RedirectToAction("Index");
}