Forgot Password + Email Confirmation in ASP.NET MVC WebMatrix [closed] - asp.net-mvc

It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 9 years ago.
It appears that these two features have a set of helper functions and schema in the WebMatrix code to get going. However, there are no controller methods or views to get it done, so you have to implement yourself.
Is there any samples anywhere where I can just copy this code into my app? I'm looking for something to:
Generate Forgot Password Email
Generate Confirmation Email
Forgot Password view + Controller method
Resend confirmation email view + Controller method

FORGOT PASSWORD FUNCTIONALITY
The other day I was trying to create a “forgot password functionality” in asp.net MVC 4. I googled in and out but couldn't get the best solution. I have finally found the way out.
15 simple steps
Part 1
Sending Password Reset Information via Email
Step 1
• Create Mvc 4 c# Internet application template :)
(Account and home controllers will automatically be generated)
• Build and run your project. Register and login.
(Simple membership tables will be generated)
Everything working fine?
Step 2
• Oops!! They don’t ask our email id while registration! In order to send password token to users we need their email id!! So let’s make a few changes in database go to server explorer! ( If u can’t find it u can press Ctrl + alt + S )
• Expand "data connections" and u will see a couple of tables. Open User Profile table.
Add the following columns:
EmailId nvarchar(max)
2.Details nvarchar(max)
Step 3
• Now go to Solution Explorer...Models ... Account model ... Register model
• Add these two properties for Email Id and Details
//new properties
[Required]
[Display(Name="Email ID")]
public string EmailId { get; set; }
[Required]
[Display(Name = "About Yourself")]
public string Details { get; set; }
Step 4
• Now go to Solution Explorer…Views ... Account Views ... Register.cshtml view
• Add these two properties for allowing users to enter email id and other details.
#Html.LabelFor(m => m.EmailId)
#Html.TextBoxFor(m => m.EmailId)
#Html.LabelFor(m => m.Details)
#Html.TextBoxFor(m => m.Details)
Step 5
• Now go to Solution Explorer…Controllers ... Account Controller ... Post version of Register controller action method
• Add these properties for allowing users to enter email id and other details.The changes are highlighted.
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Register(RegisterModel model)
{
if (ModelState.IsValid)
{
// Attempt to register the user
try
{
WebSecurity.CreateUserAndAccount(model.UserName, model.Password, new { EmailId = model.EmailId, Details = model.Details});
WebSecurity.Login(model.UserName, model.Password);
return RedirectToAction("Index", "Home");
}
catch (MembershipCreateUserException e)
{
ModelState.AddModelError("", ErrorCodeToString(e.StatusCode));
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
Why don’t we again build and run our project? Register and fill in the details .Now you will be asked to specify email address also .Add these properties for allowing users to enter email id and other details.
Go to server explorer and right click on User Profile table and Select “Show Table Data” U can view the details you entered for verification.
Step 6
• Now lets implement the password reset functionality  Go to account controller and create the following controller action method (GET )
[AllowAnonymous]
public ActionResult ForgotPassword()
{
return View();
}
• (POST)
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult ForgotPassword(string UserName)
{
//check user existance
var user = Membership.GetUser(UserName);
if (user == null)
{
TempData["Message"] = "User Not exist.";
}
else
{
//generate password token
var token = WebSecurity.GeneratePasswordResetToken(UserName);
//create url with above token
var resetLink = "<a href='" + Url.Action("ResetPassword", "Account", new { un = UserName, rt = token }, "http") + "'>Reset Password</a>";
//get user emailid
UsersContext db = new UsersContext();
var emailid = (from i in db.UserProfiles
where i.UserName == UserName
select i.EmailId).FirstOrDefault();
//send mail
string subject = "Password Reset Token";
string body = "<b>Please find the Password Reset Token</b><br/>" + resetLink; //edit it
try
{
SendEMail(emailid, subject, body);
TempData["Message"] = "Mail Sent.";
}
catch (Exception ex)
{
TempData["Message"] = "Error occured while sending email." + ex.Message;
}
//only for testing
TempData["Message"] = resetLink;
}
return View();
}
• The GET controller action just returns the view.
• The POST controller action :
Receives the username
Verifies its existence
Generates Password reset token
Builds URL to be emailed.
Step 7
• Right click on the forgot password action method and add view  The code for the view page will be as below
#{
ViewBag.Title = "Forgot Password";
}
<h2>Forgot Password</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<fieldset>
<legend>Forgot Password Form</legend>
<ol>
<li>
#Html.Label("User Name", new { #for = "UserName" })
#Html.TextBox("UserName")
<span style="color:red;">#TempData["Message"]</span>
</li>
</ol>
<input type="submit" value="Recover" />
</fieldset>
}
• The view page will display a textbox where in user can enter the user name.
Step 8
• Now go to Solution Explorer...Models ... Account model … User Profile View Model. Changes have been highlighted
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
//new properties
public string EmailId { get; set; }
public string Details { get; set; }
}
Step 9
• Now go to Solution Explorer...Views ... Account … Login View.
Now we can see an option to recover his password in case he has forgotten it.
<ul>
<li>
#Html.ActionLink("Register", "Register") if you don't have an account.
</li>
<li>
#Html.ActionLink("Forgot Password", "ForgotPassword") if you want to recover your password.
</li>
</ul>
Part 2
Receiving Password Reset Information from URL
Step 1
• Go to Solution Explorer...Controller ... Account Controller …
Create new Reset Password Action Method
• This method is accepting ‘un’ (which is username) and ‘rt’ (which is password reset token) from the URL.
[AllowAnonymous]
public ActionResult ResetPassword(string un, string rt)
{
UsersContext db = new UsersContext();
//TODO: Check the un and rt matching and then perform following
//get userid of received username
var userid = (from i in db.UserProfiles
where i.UserName == un
select i.UserId).FirstOrDefault();
//check userid and token matches
bool any = (from j in db.webpages_Memberships
where (j.UserId == userid)
&& (j.PasswordVerificationToken == rt)
//&& (j.PasswordVerificationTokenExpirationDate < DateTime.Now)
select j).Any();
if (any == true)
{
//generate random password
string newpassword = GenerateRandomPassword(6);
//reset password
bool response = WebSecurity.ResetPassword(rt, newpassword);
if (response == true)
{
//get user emailid to send password
var emailid = (from i in db.UserProfiles
where i.UserName == un
select i.EmailId).FirstOrDefault();
//send email
string subject = "New Password";
string body = "<b>Please find the New Password</b><br/>" + newpassword; //edit it
try
{
SendEMail(emailid, subject, body);
TempData["Message"] = "Mail Sent.";
}
catch (Exception ex)
{
TempData["Message"] = "Error occured while sending email." + ex.Message;
}
//display message
TempData["Message"] = "Success! Check email we sent. Your New Password Is " + newpassword;
}
else
{
TempData["Message"] = "Hey, avoid random request on this page.";
}
}
else
{
TempData["Message"] = "Username and token not maching.";
}
return View();
}
Step 2
• Right click on the reset password action method and add view  The code for the view page will be as below
#{
ViewBag.Title = "ResetPassword";
}
<h2>Password Mailed :) </h2>
Step 3
• Go to Solution Explorer...Models... Account Models …
Make the following changes.
• We create an instance of UserProfile DB Model and implement db.webpages_Memberships’ as DbSet.Use ‘webpages_Memberships’ as a model.
public class UsersContext : DbContext
{
public UsersContext()
: base("DefaultConnection")
{
}
public DbSet<UserProfile> UserProfiles { get; set; }
public DbSet<webpages_Membership> webpages_Memberships { get; set; }
}
[Table("webpages_Membership")]
public class webpages_Membership
{
[Key]
public int UserId { get; set; }
public DateTime CreateDate { get; set; }
public string ConfirmationToken { get; set; }
public bool IsConfirmed { get; set; }
public DateTime LastPasswordFailureDate { get; set; }
public int PasswordFailuresSinceLastSuccess { get; set; }
public string Password { get; set; }
public DateTime PasswordChangeDate { get; set; }
public string PasswordSalt { get; set; }
public string PasswordVerificationToken { get; set; }
public DateTime PasswordVerificationTokenExpirationDate { get; set; }
}
Step 4
• Add the Random Password Generation Function to the account controller
• This method when called will generate a random password for the user
private string GenerateRandomPassword(int length)
{
string allowedChars = "abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ0123456789!#$?_-*&#+";
char[] chars = new char[length];
Random rd = new Random();
for (int i = 0; i < length; i++)
{
chars[i] = allowedChars[rd.Next(0, allowedChars.Length)];
}
return new string(chars);
}
Step 5
• Add the Send Email Function in account controller.
• This function will send first mail to user when user clicks on recover button on forgot password form. The first mail contains the reset password link. When user clicks on the link. User will be redirected to the reset password page. Again the new password will be mailed to the user.
• You need to put in your email address in place of XXXXX#gmail.com and write your password.
private void SendEMail(string emailid, string subject, string body)
{
System.Net.Mail.SmtpClient client = new System.Net.Mail.SmtpClient();
client.DeliveryMethod = System.Net.Mail.SmtpDeliveryMethod.Network;
client.EnableSsl = true;
client.Host = "smtp.gmail.com";
client.Port = 587;
System.Net.NetworkCredential credentials = new System.Net.NetworkCredential("xxxxx#gmail.com", "password");
client.UseDefaultCredentials = false;
client.Credentials = credentials;
System.Net.Mail.MailMessage msg = new System.Net.Mail.MailMessage();
msg.From = new MailAddress("xxxxx#gmail.com");
msg.To.Add(new MailAddress(emailid));
msg.Subject = subject;
msg.IsBodyHtml = true;
msg.Body = body;
client.Send(msg);
}

Related

pass model to view and back to a new controller

I have made a password reset controller that takes in a GUID querystring. As you can see in the below code, my controller checks if the GUID value exists in my db, whereafter it returns user to the view with the attributes name and userId.
[HttpGet]
public ActionResult RetreivePass(string ac)
{
MySqlContext db = new MySqlContext();
PwRetreival PwRetreival = new PwRetreival();
Users user = new Users();
if (db.PwRetreival.FirstOrDefault(u => u.Token == ac) != null)
{
PwRetreival.uId = db.PwRetreival.Where(u => u.Token == ac).Select(u => u.uId).First();
var Attributes = db.Users.Where(u => u.UserId == PwRetreival.uId).Select(u => new { u.Name, u.UserId }).FirstOrDefault();
user.Name = Attributes.Name;
user.UserId = Attributes.UserId;
return View(user);
}
return RedirectToAction("Index", "Home");
}
In my view, it looks like this:
<p>Hello #Model.Name</p>
#using (Html.BeginForm(FormMethod.Post))
{
<p>Change password</p>
#Html.TextBoxFor(u => u.Password)
<p>
<button type="submit">Register</button>
</p>
}
And finally, upon submit, i call this controller:
[HttpPost]
public string RetreivePass(Users model)
{
//Some code here
}
Unfortunately, my issue is, that the parameters (Name & UserId) are not returned with the model to the controller on submit. Therefore i only have the password, which i can't use for anything, because i cant tell my controller where to put the updated password.
Am i missing something??
For convenience, my models are as follows:
public class Users
{
[Key]
public int UserId { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public string Name { get; set; }
public short EmailConfirmed { get; set; }
}
public class PwRetreival
{
[Key]
public int Id { get; set; }
public int uId { get; set; } //FK to Users.UserId
public string Token { get; set; }
}
Make sure you include the name into the form as some input parameter (hidden if the user is not supposed to modify it):
#using (Html.BeginForm(FormMethod.Post))
{
<p>Change password</p>
#Html.HiddenFor(u => u.UserId)
#Html.TextBoxFor(u => u.Password)
<p>
<button type="submit">Register</button>
</p>
}
So basically all parameters that you hope to retrieve when you postback must be included either as input fields inside your form or as query string parameters.
Of course you realize that a hidden field or a query string parameter can be modified from the outside making it quite insecure. Anyone knowing the UserId of another user could trigger a password reset for him. In order to make a secure password reset functionality you might need some cryptography involved. For example you could use a symmetric cryptographic algorithm to encrypt the UserId and send the encrypted value to the client in the password reset email. Then when the form is POSTed back the encrypted value could be decrypted on the server to extract the original client id and the new password. If the encrypted value has been tampered with you will know that immediately.
Another approach would be to generate some token on the server which will be associated with your user and stored in your backend. Then send this token to the client in the password reset email and when POSTed back you will know who the original user was by looking at your backend. It is also a good idea to sign this token using a server secret to ensure that it hasn't been tampered with.

During adding new object to database inside AccountController An entity object cannot be referenced by multiple instances of IEntityChangeTracker

The AccountController has been created through template by Visual Studio. I want to record some actions made by user, one of them is logging in. The ApplicationUserAction holds info about action taken:
public class ApplicationUserAction {
public int Id { get; set; }
public String Description { get; set; }
public DateTime TimeStamp { get; set; }
public virtual ApplicationUser Actor { get; set; }
}
Inside POST Login method I add new action to the db then want to save it:
protected WebApplication2.Models.ApplicationDbContext db { get; set; }
public AccountController() {
db = new ApplicationDbContext();
}
...
...
...
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl) {
if (ModelState.IsValid) {
var user = await UserManager.FindAsync(model.Email, model.Password);
if (user != null) {
await SignInAsync(user, model.RememberMe);
//MY CODE
Debug.WriteLine("HERE CODE");
ApplicationUserAction action = new ApplicationUserAction { Description = "Logged in at " + DateTime.Now + " from IP " + Request.UserHostAddress + ".", TimeStamp = DateTime.Now, Actor = user };
db.ApplicationUserActions.Add(action);
db.SaveChanges();
//END OF MY CODE
return RedirectToLocal(returnUrl);
} else {
ModelState.AddModelError("", "Invalid username or password.");
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
but when I login I get to my web application I get this exception:
An entity object cannot be referenced by multiple instances of IEntityChangeTracker.
Exception Details: System.InvalidOperationException: An entity object cannot be referenced by multiple instances of IEntityChangeTracker.
This line caused exception: db.ApplicationUserActions.Add(action);.
What went wrong, I didn't have any problems with adding something to the database yet.
By the looks if it, when you create your action, you reference user, which is being tracked via another context (no code from UserManager, so it's hard to tell).
You'd have either detach the user from the previous context, or query a new instance of it from the current context.
EDIT: Content provided by the Original Poster:
This did the job.
ApplicationUser userSecondInstance = db.Users.Find(user.Id);
ApplicationUserAction action = new ApplicationUserAction { Description = "Logged in at " + DateTime.Now + " from IP " + Request.UserHostAddress + ".", TimeStamp = DateTime.Now, Actor = userSecondInstance };
db.ApplicationUserActions.Add(action);
db.SaveChanges();

Get name and email details when doing Facebook sign-up in ASP.Net MVC (Microsoft.AspNet.Identity.EntityFramework)

I have managed to get Facebook sign-up and sign-in working using the following tutorial.
http://www.asp.net/mvc/tutorials/mvc-5/create-an-aspnet-mvc-5-app-with-facebook-and-google-oauth2-and-openid-sign-on
I have also read on the Facebook App Development site about request permissions for details such as name and email...
https://developers.facebook.com/docs/facebook-login/permissions
In relation to this, I found the "Configure App Center Permissions" button on the "App Details" page in the Facebook developer centre. I was able to add "email" as requested permission. It is my understanding the first name and last name are available from the public profile.
I don't know how long these settings take to filter through, but when I went to sign-up with Facebook it said that it was only requesting my public profile and friends list, no email.
Razor Code
That Razor code from my external login partial view looks like this (standard view from the project template...HTML has been removed)...
#using Microsoft.Owin.Security
...
#{
var loginProviders;
var context = Context.GetOwinContext();
loginProviders = context.Authentication.GetExternalAuthenticationTypes();
if (loginProviders.Count() > 0)
{
string action = Model.Action // ExternalLogin;
string returnUrl = Model.ReturnUrl;
using (Html.BeginForm(action, "Account", new { ReturnUrl = returnUrl }))
{
#Html.AntiForgeryToken()
#foreach (AuthenticationDescription p in loginProviders)
{
#* create submit button using p.AuthenticationType
}
}
}
}
Account Controller
The account controller code that handles this request is ("provider" is Facebook which is the AuthenticationType from the form):
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult ExternalLogin(string provider, string returnUrl)
{
return new ChallengeResult(
provider,
Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl }));
}
Account Controller - ChallengeResult
private class ChallengeResult : HttpUnauthorizedResult
{
public ChallengeResult(
string provider,
string redirectUri)
: this(provider, redirectUri, null)
{
}
public ChallengeResult(
string provider,
string redirectUri,
string userId)
{
this.LoginProvider = provider;
this.RedirectUri = redirectUri;
this.UserId = userId;
}
public string LoginProvider { get; set; }
public string RedirectUri { get; set; }
public string UserId { get; set; }
public override void ExecuteResult(ControllerContext context)
{
var properties = new AuthenticationProperties() { RedirectUri = RedirectUri };
if (UserId != null)
{
properties.Dictionary["XsrfId"] = UserId;
}
var authContext = context.HttpContext.GetOwinContext();
authContext.Authentication.Challenge(properties, LoginProvider);
}
}
I get the feeling that something needs to be done in the ChallengeResult class. Perhaps add extra AuthenticationProperties.
Does anyone know what I am missing?
If you add "email" criterion in FacebookAuthenticationOptions's scope in MVC 5, it requests email permission from users. You didn't post your Startup.Auth.cs but I think you used default Facebook Authentication.
Try using custom FacebookAuthentication options, like that:
Startup.Auth.cs
//app.UseTwitterAuthentication(
// consumerKey: "",
// consumerSecret: "");
//Customizing FacebookAuthenticationOptions...
var facebookOptions=new Microsoft.Owin.Security.Facebook.FacebookAuthenticationOptions()
{
AppId = "***",
AppSecret = "***"
};
facebookOptions.Scope.Add("email"); //We want to get user's email information by adding it in scope.
app.UseFacebookAuthentication(facebookOptions);

Custom user Login in ASP.NET MVC 4

I'm developing an ASP.NET MVC 4 Application, wherein I need every user to be redirected to his custom page upon login. The users are obtained from the UserProfile class which I have refactored into a separate class file. How do I modify the Redirect To method in the Login (post) Action in a ASP.NET MVC 4 Internet Project to get this functionality? Further how do I pass this data to a User controller that can display information related to this specific user.
I'm using simple Membership as it comes out of the box in an internet application template in ASP.NET MVC 4.
I'm guessing you're talking about this piece of code in the MVC4 template? I'm doing something very similar - upon login, I redirect the user to a page called Index.cshtml listed under the Account controller :
[HttpPost, AllowAnonymous, ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model, string returnUrl)
{
if (ModelState.IsValid && WebSecurity.Login(model.UserName, model.Password, model.RememberMe))
{
return RedirectToAction("Index", "Account");
}
// If we got this far, something failed, redisplay form
ModelState.AddModelError(string.Empty, LocalizedText.Account_Invalid_User_Or_Password);
return View(model);
}
For user specific data, why not just extend the UsersContext.cs class in the Classes folder, then use WebSecurity.CurrentUserId to retrieve the information that pertains to that user?
Extended UsersContext class :
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
public string Name { get; set; }
public bool IsPromotional { get; set; }
public bool IsAllowShare { get; set; }
}
This is the Index() action on the Account controller that they get redirected to upon login. Here I just call the users context, new up an AccountModel that's bound to the Index.cshtml page, set those attributes in the model, then return the View with the model we've built :
public ActionResult Index()
{
//New up the account model
var account = new AccountModel();
try
{
//Get the users context
var CurrentUserId = WebSecurity.CurrentUserId;
var context = new UsersContext();
var thisUser = context.UserProfiles.First(p => p.UserId == CurrentUserId);
//Set the name
account.Name = thisUser.Name;
//Set the user specific settings
account.IsAllowShare = thisUser.IsAllowShare;
account.IsPromotional = thisUser.IsPromotional;
}
catch (Exception exception)
{
_logger.Error(exception, "Error building Account Model");
}
return View(account);
}
It may not be exactly what you're looking for, but that should get you moving in the right direction.

Custom validation error not displayed

I have an MVC 3 application and am trying to display a custom validation error. The normal validation errors that are generated by the model, i.e. Required, are displayed on the page. Now I am checking if a user exists and if so, adding a error message:
if (userExists)
ModelState.AddModelError("UserName", UserManagementResources.UserAlreadyExistsText);
return View(model);
On the view I have a validation summary and a Html.ValidationMessage("UserName"), but neither one is displaying the error. I have used this successfully on other pages. The only difference with this page I can see is, that it uses the RequiredIf validator scripts.
http://blogs.msdn.com/b/simonince/archive/2011/02/04/conditional-validation-in-asp-net-mvc-3.aspx
Any ideas how to solve this problem are appreciated. Thanks.
Edit
I am returning the validation message through the Remote validation. If I look what the network is doing, it's returning the error message, but it is still not displayed on the view.
[Required]
[DataType(DataType.EmailAddress)]
[Remote("IsUserAvailable", "Validation", ErrorMessage = "Ein Benutzer mit dieser Email existiert bereits.")]
[Display(Name = Resources.EmailText, ResourceType = typeof(Resources))]
public string Email
{
get { return User.Email; }
set { User.Email = value; }
}
The View:
#Html.LabelFor(u => u.Email, Resources.Email + " (Login) *")
#Html.EditorFor(u => u.Email)
#Html.ValidationMessageFor(u => u.Email)
<br clear="all" />
The Remote Validation Controller:
public class ValidationController : Controller
{
public JsonResult IsUserAvailable(string Email)
{
bool userExists;
using (var userModel = new UserManagementModel())
{
userExists = userModel.UserExists(Email);
}
if(userExists)
return Json(UserManagementResources.UserAlreadyExists, JsonRequestBehavior.AllowGet);
else
return Json(true, JsonRequestBehavior.AllowGet);
}
}
Why don't you use the Remote validation for this?
Why posting back just to check if user exists?
example:
public class RegisterModel
{
[Required]
[Remote("UserNameExists", "Validation", "", ErrorMessage = "Username is already taken.")]
[RegularExpression(#"(\S)+", ErrorMessage = "White space is not allowed.")]
[Display(Name = "Username")]
public string UserName { get; set; }
}
and create a Validation Controller having the UserNameExists method like
public JsonResult UserNameExists(string UserName)
{
var user = _db.Users.Where(x => x.username.Equals(UserName));
return user == null ?
Json(true, JsonRequestBehavior.AllowGet) :
Json(string.Format("{0} is not available.", register.UserName), JsonRequestBehavior.AllowGet);
}
When you change the version of your jQuery.js you have to change the validation.js file as well. Different versions are not compatible to each other and you might see strange behaviour in different browsers when you mixup the files.

Resources