I have modified the Views/Manage/Index.cshtml to display the User's email as well. I've modified the IndexViewModel as well so it recognizes the "Email" string and then made another .cshtml page similar to the changing of phone number one which is there by default. The new page is called ChangeEmail.cshtml
#using (Html.BeginForm("ChangeEmail", "Manage", FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
#Html.AntiForgeryToken()
<h4>Add an email</h4>
<hr />
#Html.ValidationSummary("", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Email, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(model => model.Email, 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="Submit" />
</div>
</div>
}
From what I've seen, the changing of password happens through a task called "ChangePasswordAsync" inside UserManager.cs
Is there a way to change the Email without making a new task?
EDIT: added more from the controller(index) :
public async Task<ActionResult> Index(ManageMessageId? message)
{
ViewBag.StatusMessage =
message == ManageMessageId.ChangePasswordSuccess ? "Your password has been changed."
: message == ManageMessageId.SetPasswordSuccess ? "Your password has been set."
: message == ManageMessageId.SetTwoFactorSuccess ? "Your two-factor authentication provider has been set."
: message == ManageMessageId.Error ? "An error has occurred."
: message == ManageMessageId.AddPhoneSuccess ? "Your phone number was added."
: message == ManageMessageId.RemovePhoneSuccess ? "Your phone number was removed."
: message == ManageMessageId.EmailChangedSuccess ? "Your email has been changed"
: "";
var userId = User.Identity.GetUserId();
var userEmail = User.Identity.Name;
var user = UserManager.FindById(userId);
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),
Email = user.Email,
City = user.City,
Region = user.Region
};
user.Email = "topkek#ucn.dk";
UserManager.UpdateAsync(user);
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ChangeEmail(ChangeEmailViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
return RedirectToAction("Index", new { Message = ManageMessageId.EmailChangedSuccess });
}
Get the user's Email address from your ChangeEmailViewModel and then update the user's details using the userManager.UpdateAsync(user)
EDIT
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ChangeEmail(ChangeEmailViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
//Get the user's Id
var userId = User.Identity.GetUserId();
//get the user (you can modify the variables to fit yours)
var user = UserManager.FindByIdAsync(userId);
//this is how to change the Email
user.Result.Email= model.Email// **EDIT**;
userManager.UpdateAync(user.Result);
return RedirectToAction("Index", new { Message = ManageMessageId.EmailChangedSuccess });
}
Related
I'm new to ASP.NET MVC 5 and I'm finding very uncomfortable with Identity authentication + authorization framework. I know this is a new feature of the ASP.NET MVC framework, so I'd like to apply an alternative way to implement authentication in m y application.
Is it possible? I read I could use the FormsAuthenticationModule. Is this a good alternative? How can I use it in a MVC 5 based application?
I felt the same way when taking a look at Identity. It adds lots of unnecessary abstractions and does not suit with my case that I have legacy system which implemented customised authentication work-flow.
Tons of examples out there about OWIN authentication using Identity and EF by default which makes developers confused that OWIN has to go with Identity and Entity Framework.
But technically, you are able to strip out Identity to use only OWIN cookie authentication (Microsoft.Owin.Security.Cookies). The code turns out very simple, below is example I got from my code which eliminates trivial things:
[HttpPost]
public ActionResult Login(LoginViewModel model, string returnUrl)
{
var user = _userService.GetByEmail(model.Email);
//check username and password from database, naive checking:
//password should be in SHA
if (user != null && (user.Password == model.Password))
{
var claims = new[] {
new Claim(ClaimTypes.Name, user.Name),
new Claim(ClaimTypes.Email, user.Email),
// can add more claims
};
var identity = new ClaimsIdentity(claims, "ApplicationCookie");
// Add roles into claims
var roles = _roleService.GetByUserId(user.Id);
if (roles.Any())
{
var roleClaims = roles.Select(r => new Claim(ClaimTypes.Role, r.Name));
identity.AddClaims(roleClaims);
}
var context = Request.GetOwinContext();
var authManager = context.Authentication;
authManager.SignIn(new AuthenticationProperties
{ IsPersistent = model.RememberMe }, identity);
return RedirectToAction("Index", "Home");
}
// login failed.
}
public ActionResult LogOut()
{
var ctx = Request.GetOwinContext();
var authManager = ctx.Authentication;
authManager.SignOut("ApplicationCookie");
return RedirectToAction("Login");
}
Without Using Owin Security Methods:
Itz My Controller Coding
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Login(Employee emp, string returnUrl)
{
using(AdaptiveProjectEntities db = new AdaptiveProjectEntities())
{
string email = emp.Email;
// byte[] en = System.Text.Encoding.UTF8.GetBytes(emp.Password);
//var ee = Convert.ToBase64String(en);
string pass = emp.Password;
bool userValid = db.Employees.Any(user => user.Email == email && user.Password == pass);
if(userValid)
{
FormsAuthentication.SetAuthCookie(email, false);
if (Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/")
&& !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\\"))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Projects");
}
}
else
{
ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
}
return View(emp);
}
public ActionResult Logout()
{
FormsAuthentication.SignOut();
return RedirectToAction("Login", "Login");
}
}
}
View:
<div class="container" style="margin-right:50%">
<div class="row">
<div class="col-md-12 col-md-offset-7" style="bottom:-250px">
<div class="panel panel-default" style="margin-right:15%">
<div class="panel-heading" style="padding-bottom:5%">
<center><h3 style="margin-right:80px">Login</h3></center>
#*</div>*#
#using (Html.BeginForm())
{
<div class="modal-body">
#Html.AntiForgeryToken()
<div class="form-horizontal" style="margin-right: 10%;">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Email, htmlAttributes: new { #class = "control-label col-md-3" })
<div class="col-md-9">
#Html.EditorFor(model => model.Email, new { htmlAttributes = new { #class = "form-control", type = "email", required = "required" } })
#Html.ValidationMessageFor(model => model.Email, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Password, htmlAttributes: new { #class = "control-label col-md-3" })
<div class="col-md-9">
#Html.EditorFor(model => model.Password, new { htmlAttributes = new { #class = "form-control", type = "password", required = "required" } })
#Html.ValidationMessageFor(model => model.Password, "", new { #class = "text-danger" })
</div>
</div>
</div>
<div>
<input class="btn btn-primary pull-left col-lg-offset-1" type="submit" value="Login" style="margin-left:35%" />
</div>
</div>
}
</div>
</div>
</div>
</div>
</div>
</div>
I am developing a basic web site in ASP.NET MVC 5 (using visual studio 2013). The site will use Facebook for user authentication and for retrieving initial profile data.like stack over flow login with facebook..access user name,profile photo etc..plz any one help me relevant sample
startup.auth.cs
FacebookAuthenticationOptions fbao = new FacebookAuthenticationOptions();
fbao.AppId = "****";
fbao.AppSecret = "*****";
fbao.Scope.Add("email");
fbao.Scope.Add("user_birthday");
fbao.Scope.Add("user_hometown");
fbao.SignInAsAuthenticationType = Microsoft.Owin.Security.AppBuilderSecurityExtensions.GetDefaultSignInAsAuthenticationType(app);
app.UseFacebookAuthentication(fbao);
Account controller>>Externallogincallback
// GET: /Account/ExternalLoginCallback
[AllowAnonymous]
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
ClaimsIdentity fboa = await AuthenticationManager.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie);
//var email = ext.Claims.First(x => x.Type.Contains("emailaddress")).Value;
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
if (loginInfo == null)
{
return RedirectToAction("Login");
}
// Sign in the user with this external login provider if the user already has a login
var result = await SignInManager.ExternalSignInAsync(loginInfo, isPersistent: false);
switch (result)
{
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = false });
case SignInStatus.Failure:
default:
// If the user does not have an account, then prompt the user to create an account
ViewBag.ReturnUrl = returnUrl;
ViewBag.LoginProvider = loginInfo.Login.LoginProvider;
return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = loginInfo.Email });
}
}
Account view model
public class ExternalLoginConfirmationViewModel
{
[Required]
[Display(Name = "Email")]
public string Email { get; set; }
public string UserName { get; set; }
}
externalloginconfirmationview
<h4>Association Form</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<p class="text-info">
You've successfully authenticated with <strong>#ViewBag.LoginProvider</strong>.
Please enter a user name for this site below and click the Register button to finish
logging in.
</p>
<div class="form-group">
#Html.LabelFor(m => m.Email, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.Email, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Email, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.UserName, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.UserName, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.UserName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" class="btn btn-default" value="Register" />
</div>
</div>
Following are some nice tutorials that may help solve your issue
http://www.asp.net/mvc/overview/security/create-an-aspnet-mvc-5-app-with-facebook-and-google-oauth2-and-openid-sign-on
http://www.asp.net/mvc/overview/getting-started/aspnet-mvc-facebook-birthday-app
Im trying to send an email to a user when they have forgot their password in a mvc app this is my code.pls help
this is my email class in my businesslogic
public class EmailBusiness
{
public bool SendEmailForgot(string email, string url)
{
bool isvalid = false;
try
{
var boddy = new StringBuilder();
boddy.Append("Hi! " + email + "<br/>");
boddy.Append("Click the link to reset your " + url);
string bodyFor = boddy.ToString();
string toFor = email;
const string subjectFor = "Reset Password";
WebMail.SmtpServer = "pod51014.outlook.com";
WebMail.SmtpPort = 587;
WebMail.UserName = "20822526#dut4life.ac.za";
WebMail.Password = "Password";
WebMail.From = "20822526#dut4life.ac.za";
WebMail.EnableSsl = true;
WebMail.Send(to: toFor, subject: subjectFor, body: bodyFor);
isvalid = true;
return isvalid;
}
catch
{
return isvalid;
}
}
this is my forgotpassword model in my Accountviewmodels
public class ForgotPasswordViewModel
{
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }
}
this is my forgotpassword action in accountcontroller
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ForgotPassword(ForgotPasswordViewModel model, string sms)
{
EmailBusiness _emailBusiness = new EmailBusiness();
if (ModelState.IsValid)
{
var user = await UserManager.FindByNameAsync(model.Email);
if (user == null || !(await UserManager.IsEmailConfirmedAsync(user.Id)))
{
// Don't reveal that the user does not exist or is not confirmed
return View("ForgotPasswordConfirmation");
}
// 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.GeneratePasswordResetTokenAsync(user.Id);
var callbackUrl = Url.Action("ResetPassword", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
await UserManager.SendEmailAsync(user.Id, "Reset Password", "Please reset your password by clicking here");
_emailBusiness.SendEmailForgot(model.Email, callbackUrl);
return RedirectToAction("ForgotPasswordConfirmation", "Account");
}
// If we got this far, something failed, redisplay form
return View(model);
}
and this is my view
#model Template.Model.ForgotPasswordViewModel
#{
ViewBag.Title = "Forgot your password?";
Layout = "~/Views/shared/_BootstrapLayout.basic.cshtml";
}
<h2>#ViewBag.Title.</h2>
#using (Html.BeginForm("ForgotPassword", "Account", FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
#Html.AntiForgeryToken()
<h4>Enter your email.</h4>
<hr />
#Html.ValidationSummary("", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(m => m.Email, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.Email, 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>
}
and this in my webconfig
<appSettings>
<add key="from" value="20822526#dut4life.ac.za" />
<add key="webpages:Version" value="3.0.0.0" />
<add key="webpages:Enabled" value="false" />
<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" />
I've created a new Web Forms Project in visual studio 2013 and I want users to be able to login using their UserName(eg. administrator) not their email address.
If you open the automatically generated "Register.aspx.cs" file you'll see this piece of code:
var manager = Context.GetOwinContext().GetUserManager<ApplicationUserManager>();
var signInManager = Context.GetOwinContext().Get<ApplicationSignInManager>();
var user = new ApplicationUser() { UserName = Email.Text, Email = Email.Text };
Question:
Why does the UserName and Email have the same value on last line??? In this case why do you need UserName column since you have already Email column?
Is possible to login using username not email address?
I'm trying to solve this by adding a new textbox field to registration form called "UserName":
Register.aspx
<div class="form-group">
<asp:Label runat="server" AssociatedControlID="UserName" CssClass="col-md-2 control-label">UserName</asp:Label>
<div class="col-md-10">
<asp:TextBox runat="server" ID="UserName" CssClass="form-control" />
<asp:RequiredFieldValidator runat="server" ControlToValidate="Email"
CssClass="text-danger" ErrorMessage="The UserName field is required." />
</div>
</div>
And on the Register function I've made this change:
Register.aspx.cs
var user = new ApplicationUser() { UserName = UserName.Text, Email = Email.Text };
Then I've changed the login function like this:
Login.aspx.cs
protected void LogIn(object sender, EventArgs e)
{
if (IsValid)
{
// Validate the user password
var manager = Context.GetOwinContext().GetUserManager<ApplicationUserManager>();
var signinManager = Context.GetOwinContext().GetUserManager<ApplicationSignInManager>();
// This doen't count login failures towards account lockout
// To enable password failures to trigger lockout, change to shouldLockout: true
//var result = signinManager.PasswordSignIn(Email.Text, Password.Text, false, shouldLockout: false);
SignInStatus result = SignInStatus.Failure;
// check too see if the user has entered a email address or not
if (IsValidEmail(NameOrEmail.Text))
{
result = signinManager.PasswordSignIn(NameOrEmail.Text, Password.Text, false, shouldLockout: false);
}
else
{
// get users email using his name
var user = manager.FindByName(NameOrEmail.Text);
result = signinManager.PasswordSignIn(user.Email, Password.Text, false, shouldLockout: false);
}
switch (result)
{
case SignInStatus.Success:
string queryString = Request.QueryString["ReturnUrl"];
if (!string.IsNullOrEmpty(queryString))
IdentityHelper.RedirectToReturnUrl(Request.QueryString["ReturnUrl"], Response);
else
Response.Redirect("/Account/Dashboard");
break;
case SignInStatus.LockedOut:
Response.Redirect("/Account/Lockout");
break;
case SignInStatus.Failure:
default:
FailureTextLabel.Text = "Invalid login!";
ErrorMessage.Visible = true;
break;
}
}
}
// check email address
private bool IsValidEmail(string strIn)
{
// Return true if strIn is in valid e-mail format.
return Regex.IsMatch(strIn, #"^([\w-\.]+)#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$");
}
And the template looks like this:
Login.aspx
<div class="form-group">
<asp:Label runat="server" AssociatedControlID="NameOrEmail" CssClass="col-md-2 control-label">UserName or Email</asp:Label>
<div class="col-md-10">
<asp:TextBox runat="server" ID="Email" CssClass="form-control" />
<asp:RequiredFieldValidator runat="server" ControlToValidate="NameOrEmail"
CssClass="text-danger" ErrorMessage="The name or email field is required." />
</div>
</div>
The problem:
If you change the UserName on Register you cannot login anymore(you'll get SignInStatus.Failure).
Seems to me that the UserName has to be the same as Email on registration for the login to work.
Is there a way to solve this?
First of all this isn't a bug at all. This is how it works, if you want to use out of the box default implementation which created with the visual studio 2013 template (with individual users) then you will get it that way.
As developers asp.net identity stuff can be configurable according to our choice. For that you need to go deep into identity framework and understand how it works.
Most of simple yet very important configuration can be found in the IdentityConfig.cs file if you have used visual studio 2013 with update 3 or later.
identity framework version 1 comes with only with username without email address supporting UI, but things changed with the version 2.x and visual studio 2013 update 3 and 4 which included email address for both username and email.
You can change it to use username instead email as explained below.
New out of the box registration code is:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.Email, Email = model.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);
}
You can see that the Username and Email both uses model.Email.
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
Update your code as below
Register method
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
UserManager.UserValidator = new UserValidator<ApplicationUser>(UserManager)
{
RequireUniqueEmail = false,
};
var user = new ApplicationUser { UserName = model.UserName };
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);
}
RegisterViewModel
public class RegisterViewModel
{
[Required]
[Display(Name = "UserName")]
public string UserName { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
LoginViewModel
public class LoginViewModel
{
[Required]
[Display(Name = "UserName")]
public string UserName { get; set; }
[Required]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[Display(Name = "Remember me?")]
public bool RememberMe { get; set; }
}
Further you need to update other view models not to use email and instead use username
[Required]
[Display(Name = "UserName")]
public string UserName { get; set; }
Then update all views to use UserName instead Email like below.
#model WebApplication2.Models.RegisterViewModel
#{
ViewBag.Title = "Register";
}
<h2>#ViewBag.Title.</h2>
#using (Html.BeginForm("Register", "Account", FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
#Html.AntiForgeryToken()
<h4>Create a new account.</h4>
<hr />
#Html.ValidationSummary("", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(m => m.UserName, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.UserName, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.Password, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.PasswordFor(m => m.Password, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.ConfirmPassword, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.PasswordFor(m => m.ConfirmPassword, 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="Register" />
</div>
</div>
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
In my application I am trying to get a forgot password to work. I am trying to send an email to the user when he/she inputs his or her user name, sends them and email, they click on that link, and are brought back into the website and changes their password. Only thing is that my linq query is wrong an is not checking to see if the username exists in the database. Is this the right way to go by getting a forget password to work?
Here is my code
Controller
// GET: /Account/ForgotPassword
[AllowAnonymous]
public ActionResult ForgetPassword()
{
return View();
}
// Post: /Account/ForgotPassword
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult ForgetPassword(ForgetPasswordModel model, string UserName)
{
string emailAddress = (from i in db.UserProfiles
where i.UserName.Equals(model.Username)
select i.Email).Single();
if (!string.IsNullOrEmpty(emailAddress))
{
string confirmationToken =
WebSecurity.GeneratePasswordResetToken(model.Username);
dynamic email = new Email("ChngPasswordEmail");
email.To = emailAddress;
email.UserName = model.Username;
email.ConfirmationToken = confirmationToken;
email.Send();
return RedirectToAction("ResetPwStepTwo");
}
return RedirectToAction("InvalidUserName");
}
Model
public class ForgetPasswordModel
{
[Required]
[Display(Name = "Username")]
public string Username { get; set; }
}
View
#model MyFestival.Models.UserProfile
#{
ViewBag.Title = "Forgot Password";
}
<hr />
<div class="form-group">
<h3 class="panel-title">Did you forget your password?</h3>
</div>
#using (Html.BeginForm())
{
<div class="">
#Html.AntiForgeryToken()
#Html.ValidationSummary(true, null, new { #style = "color:red;" })
<hr />
<label>To reset your password, input your email address and press the Reset Password button.</label>
<br/>
<div class="form-group" >
<div class="col-md-12" >
<div class="input-group">
<span class="input-group-addon" ><i class="glyphicon glyphicon-user" ></i ></span >
#Html.TextBoxFor(m => m.UserName, new { #class = "form-control", #placeholder = "Username" })
</div >
#Html.ValidationMessageFor(m => m.UserName, null, new { #style = "color:red;" })
</div>
</div>
<br/>
<br/>
<div class="form-group" >
<div class="col-md-offset-2 col-md-10" >
<input type="submit" class="btn btn-default" value="Reset Password"/ >
#Html.ActionLink("Back to Login", "Login", null, new { #class = "btn btn-info" })
</div >
</div>
</div>
}
I've just spotted that you have both ForgetPasswordModel model and string username as arguments of your controller action and later you use Username property of your model. I assume that Model Binder just initializes the stirng username instead of ForgegPasswordModel. Could you try removing stirng username - the second parameter of ForgetPassword action?
You can create your own GUID and send it with email to user.
When user click on email link check Guid and update new password of user.
Here is the sample code
Create a view to enter forgot password email
#{
ViewBag.Title = "Forgot Password";
}
<h2>Forgot Password</h2>
#using (Html.BeginForm())
{
<div class="form-horizontal">
<hr />
<div class="text-success">
#ViewBag.Message
</div>
<div class="form-group">
Please enter your registered email address below to receive an email containing a link, to reset your password.
</div>
<div class="form-group">
<label class="control-label col-md-2">Email Address</label>
#Html.TextBox("EmailID", "", new { #class = "form-control" })
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Submit" class="btn btn-success" />
</div>
</div>
</div>
}
Then in your Controller get Email id from user, check if email exists and create new GUID, save it in database, send email to user.
[HttpPost]
public ActionResult ForgotPassword(string EmailID)
{
string resetCode = Guid.NewGuid().ToString();
var verifyUrl = "/Account/ResetPassword/" + resetCode;
var link = Request.Url.AbsoluteUri.Replace(Request.Url.PathAndQuery, verifyUrl);
//get user details from database.
using (var context = new LoginRegistrationInMVCEntities())
{
var getUser = (from s in context.RegisterUsers where s.Email == EmailID select s).FirstOrDefault();
if (getUser != null)
{
getUser.ResetPasswordCode = resetCode;
//This line I have added here to avoid confirm password not match issue , as we had added a confirm password property
context.Configuration.ValidateOnSaveEnabled = false;
context.SaveChanges();
var subject = "Password Reset Request";
var body = "Hi " + getUser.FirstName + ", <br/> You recently requested to reset your password for your account. Click the link below to reset it. " +
" <br/><br/><a href='" + link + "'>" + link + "</a> <br/><br/>" +
"If you did not request a password reset, please ignore this email or reply to let us know.<br/><br/> Thank you";
SendEmail(getUser.Email, body, subject);
ViewBag.Message = "Reset password link has been sent to your email id.";
}
else
{
ViewBag.Message = "User doesn't exists.";
return View();
}
}
return View();
}
private void SendEmail(string emailAddress, string body, string subject)
{
using (MailMessage mm = new MailMessage("youremail#gmail.com", emailAddress))
{
mm.Subject = subject;
mm.Body = body;
mm.IsBodyHtml = true;
SmtpClient smtp = new SmtpClient();
smtp.Host = "smtp.gmail.com";
smtp.EnableSsl = true;
NetworkCredential NetworkCred = new NetworkCredential("youremail#gmail.com", "YourPassword");
smtp.UseDefaultCredentials = true;
smtp.Credentials = NetworkCred;
smtp.Port = 587;
smtp.Send(mm);
}
}
Now, when user click on the email and enter new password, check Guid with what we have saved in database and update password if guid matches.
Check for more info:
Forgot password functionality in ASP.NET MVC (Reset password by Email)