Best practice to send a member their forgotten password - c#

I am building a login/registration section for our members area in Umbraco 7.1.7.
I would like to send a user their password in plain text via email.
The idea is, if the user forgets their password, they can simply enter their email address (which also serves as their username) and clicks the button:
Now, I know Umbraco uses the ASP.NET Membership Provider and hashes passwords, which is nice but I need to access the plaintext version to send in the email.
I have changed my web.config to include this:
<membership defaultProvider="UmbracoMembershipProvider" userIsOnlineTimeWindow="15">
<providers>
<clear />
<add name="UmbracoMembershipProvider"
type="Umbraco.Web.Security.Providers.MembersMembershipProvider, Umbraco"
minRequiredNonalphanumericCharacters="0"
minRequiredPasswordLength="0"
useLegacyEncoding="true"
enablePasswordRetrieval="true"
enablePasswordReset="true"
requiresQuestionAndAnswer="false"
defaultMemberTypeAlias="Member"
passwordFormat="clear" />
and my Controller Action method looks like this:
// Forgot password logic to send email
[ActionName("MvcMemberForgot")]
public ActionResult MvcMemberForgot(MvcMemberForgotModel model)
{
string emailaddress = model.Email.ToString(); // The users entered email address.
var member = Services.MemberService.GetByEmail(emailaddress);
var sb = new StringBuilder(); // To build the message.
sb.Append("Here is the password you chose for this email address: ");
sb.Append(password); // I need a password value here...
// Send the email.
library.SendMail("noreply#company.co.uk", emailaddress, "Password", sb.ToString(), true);
return CurrentUmbracoPage();
}
I can use var x = member.RawPasswordValue; to return the hashed password, is there a way to get the password the user typed in when they registered in a similar way?

I wouldn't send the emails to people in plain text, that's very poor security practice. The way I approached this recently was to do the following:
add a property to the member (a label) for storing a reset timestamp
have a reset request form that validates the user by email address, and then sets the label to a timestamp set 30 minutes to the future
send the member an email with a link to another form page, passing in their email and timestamp
on the form, get the email and timestamp, verify that the email exists, and that the timestamp hasn't expired
if they pass those tests, give them the option to enter and confirm a new password that you then save against the user (clearing the timestamp from the label at the same time)
My approach was based on the code from this example repo: https://github.com/warrenbuckley/CWS-Umbraco-Standard-Membership which contains a lot of useful examples of working with members!

Related

Altering the message sent by asp:PasswordRecovery email without loosing the password/username

I have an ASP PasswordRecovery control on my web application which was sending an email upon a successful password rest containing a short piece of text and then the username and newly reset password.
I needed to make the short piece of text bilingual so I added the following to the code behind:
protected void PasswordRecovery1_SendingMail(object sender, MailMessageEventArgs e)
{
e.Message.IsBodyHtml = false;
string body = "translated text" + Environment.NewLine + "english text";
e.Message.Body = body;
e.Message.Subject = ConfigurationManager.AppSettings["Subject"];
}
What happened then was the translated text came through but the username and password were now missing.
So I tried adding this to my body:
string pw = Membership.GetUser(PasswordRecovery1.UserName).GetPassword(PasswordRecovery1.Answer);
string p = PasswordRecovery1.UserName;
And I got this message:
This Membership Provider has not been configured to support password
retrieval.
So I added this to the control:
enablePasswordRetrieval="true"
But I still got the same message. What am I doing wrong?
If your Membership is configured to be hashed, then password retrieval is disabled.
A ConfigurationException will be thrown if enablePasswordRetrieval is set to true and passwordFormat is set to Hashed in the Web.config file for the ASP.NET application.
https://msdn.microsoft.com/en-us/library/2x0c6sfa%28v=vs.110%29.aspx
If this is the case, you should store the new password in a string, and then save this to the database using the ChangePassword() function.
I have this working like the below:
MembershipUser user = Membership.GetUser("username"); // or however you want to retrieve the MembershipUser object
string password = Membership.GeneratePassword(12, 0); // generate a new password
bool changePasswordSucceeded = user.ChangePassword(user.ResetPassword(), password); // reset the password
if(changePasswordSucceeded)
{
// email logic here
}
I had overcomplicated this massively.
As per Damien's comment you can easily solve this by creating your own PasswordReset.txt file, something like:
Translated Message
Translated word for UserName: <%UserName%> Translated word for
Password: <%Password%>
English Message
User Name: <%UserName%> Password: <%Password%>
Then just go to the PasswordRecovery Properties box and point MailDefinition-BodyFileName at your file.
As long as you have the tags as per above, the control will swap out the user name and password for you.
No need to mess around with the sending mail event in the code behind.
Hope this helps someone.

Creating Sitefinity User Password Hash

I am seeking some help on a telerik sitefinity backend feature. I'm creating users from a custom radgrid programatically. The code that I'm using to create a sitefinity user is as follows:
public MembershipCreateStatus AddUser(UserModel model, Role role)
{
var userManager = UserManager.GetManager();
var profileManager = UserProfileManager.GetManager()
var roleManager = RoleManager.GetManager("AppRoles");
MembershipCreateStatus status;
userManager.Provider.SuppressSecurityChecks = true;
var user = userManager.CreateUser(model.UserName, model.Password, model.Email,
model.SecretQuestion, model.SecretAnswer, true, null, out status);
if(status == MembershipCreateStatus.Success)
{
roleManager.AddUserToRole(user, role);
roleManager.SaveChanges();
var profile = profileManager.CreateProfile(user, Guid.NewGuid(),
typeof(SitefinityProfile)) as SitefinityProfile;
if (profile != null)
{
profile.FirstName = model.FirstName;
profile.LastName = model.LastName;
//Set any Data Extended Properties below
}
profileManager.RecompileItemUrls(profile);
profileManager.SaveChanges();
userManager.SaveChanges();
}
return status
}
This will let me create a sitefinity user and I can see that the user is stored in the sf_users table. The problem that I'm having is I need a way to lookup and send a user their password if they forget the password. The password is hashed and saved in an encrypted format in the table in the database. I've looked for documentation on how to change the password format to clear text or something of the sort but I've been unsuccessful in finding anything useful yet.
If anyone knows how to accomplish this so that I can save the password as clear text in the database then that would be really great.
Sitefinity Membership Provider is designed to work in similar way as the SQL Membership Provider. It supports three modes - Hashed (default), Encrypted and Clear. The Hashed format is default one. More information here: http://www.sitefinity.com/documentation/documentationarticles/developers-guide/deep-dive/security/users/password-format.
Hashed passwords are irreversible and you cannot achieve what you want using a hashed password format. Instead you can achieve this by either using the Clear (strongly not recommended) or the Encrypted (also not a good security practice).
However the CMS allows you to have reset password functionality or retrieve. Reset if Hashed format is used and retrieve if Encrypted is used. This article explains both approaches: http://www.sitefinity.com/developer-network/knowledge-base/configuring-password-recovery-in-sitefinity.
I don't know how SiteFinity works but it seems to use MembershipProvider. So the password format setting depends of the provider used and can be changed in the web.config file.
For example (http://msdn.microsoft.com/en-us/library/system.web.security.sqlmembershipprovider.aspx)
<system.web>
<membership defaultProvider="SqlProvider">
<providers>
<add
name="SqlProvider"
type="System.Web.Security.SqlMembershipProvider"
passwordFormat="Clear" />
</providers>
</membership>
</system.web>
Anyway, storing password in clear way is not a good practice. Instead you should provide users an interface to reset their password. At least you should generate a new password and send them the new password (of course they must be able to change it). This way the password can be hashed before saving it in the database.

Register Site User and Send Email

What is the best way to register the user for the website and send a link with user name and password?
Admin will create the user by entering user name but not password
The password needs to be generated and stored as a hash text in database and send the same to user's email with link and user name. (Here I cant reverse the hash text back to plain text and send to in email) :(
How can i achieve this? I stored some random text in a hashed format in database. Not sure how I will email to the user, whenever admin create a new user.
Any idea/articles or suggestion?
Personally I don't like the sending passwords in plain text. However I can understand why it is sometimes required. For example an admin creating an account for a user.
Sending the initial email with credentials
When the user is registered with the website. Save the email address and randomly generated password (hashed) to the database. On successful INSERT send the email to the user with the original randomly generated password (not the hashed one).
If the email fails
If the email fails to send or reach the recipient, or they delete it, then they've lost the password. Your site will need a forgotten password section where the user can request it to be reset. On performing this action your script will create another random generated password, store the hashed version to the database and send the unhashed version to the user.
It's a good idea to separate the reset password from the main account details in case it wasn't the owner who tried to reset it. Otherwise when they come to login their known password will no longer work because the reset password would have overwritten it.
Change password on login
In both scenarios the user should be forced to change password on login.
Additional Options
If you wanted you could store a timestamp along with the account credentials for how long they have to reset or login for the first time. If the login request with the emailed credentials is within that time then you allow them access. If it is not then you say sorry credentials expired and allow them to reset again.
You can use authentication for admin login and giving him authority to create new logins.
You can get help about authentication and authorizations over here>>
http://msdn.microsoft.com/en-IN/library/eeyk640h%28v=vs.100%29.aspx
and
http://www.codeproject.com/Articles/98950/ASP-NET-authentication-and-authorization
And you can simply use this function to send Email>>
public int sendMail(string to,string cc,string bcc,string subject,string body)
{
try
{
SmtpMail.SmtpServer="your_server_address";
MailMessage msg = new MailMessage();
msg.From = "your_email_id";
msg.To = to;
msg.Cc = cc;
msg.Bcc = bcc;
msg.Subject = subject;
msg.Body = body;
SmtpMail.Send(msg);
return(1);
}
catch
{
return (0);
}
}
On SendEmail Button Click>>
private void Button1_ServerClick(object sender, System.EventArgs e)
{
String to = “to_email_id”;
String cc = “cc_email_id”;
String bcc = “bcc_email_id”;
String subject = “your subject goes here”;
String body = “your body text goes here”;
int status = sendMail(to,cc,bcc,subject,body);
if(status == 1)
Response.Write("your mail has been sent successfully");
else
Response.Write("sorry! your mail could not be sent”);
}
Hope its helpful.

WebMatrix.WebData.WebSecurity - How can I get UserName by only having PasswordResetToken

I just wanted to ask for help to get my scenario work? I want to get the UserName using a PasswordResetToken.
This is my scenario.
I have a forgot password feature in my website that would send a passwordresettoken email a change password to the user.
I wanted to send just the passwordresettoken string only.
When the user clicks the link. I will just query the request["token"] to get the username and and then will allow the user to change password and autologin.
this is my code below:
public ActionResult ChangePassword()
{
ChangePasswordModel model = new ChangePasswordModel();
string token=string.Empty;
try
{
token = Request["token"].ToString();
int userId = WebSecurity.GetUserIdFromPasswordResetToken(token);
if (userId > 0)
{
//Get the user object by (userid)
//???????????????????
//???????????????????
}
else
{
throw new Exception("The change password token has expired. Please go to login page and click forgot password again.");
}
}
catch
{
model.HasError = true;
ModelState.AddModelError("", "The change password token has expired. Please go to login page and click forgot password again.");
}
return View(model);
}
Thank you in advance.
Look at the remark at the end of this article: WebSecurity.GeneratePasswordResetToken Method.
I'll copy the relevant part for your convenience:
If users have forgotten their password, they can request a new one. To
provide a new password, do the following:
Create a password-reset page that has a field where users can enter their email address.
When a user has entered his or her email address in the password-reset page, verify that the email address represents a valid
user. If it does, generate a password reset token by calling the
GeneratePasswordResetToken(String, Int32) method.
Create a hyperlink that points to a confirmation page in your site and that includes the token as a query-string parameter in the link's
URL.
Send the link to a user in an email message. When the user receives the email message, he or she can click the link to invoke the
confirmation page.
Create a confirmation page that extracts the token from the URL parameter and that lets the user enter a new password.
When the user submits the new password, call the ResetPassword(String, String) method and pass the password reset token
and the new password. If the token is valid, the password will be
reset. If the token is not valid (for example, it has expired),
display an error message.
Highlighting is mine. Basically you do not need the user name. The framework does all the heavy lifting for you.
Addressing your comment, I would not recommend automatically logging the user in. It's a good practice for them to log manually to check that this password changing thingie has actually worked, and not to discover that it did not only next time around.
Anyway, you can do this:
SimpleMembershipProvider provider = (SimpleMembershipProvider)Membership.Provider;
string username = provider.GetUserNameFromId(userId);
Reference: GetUserNameFromId.
I think the WebSecurity.GetUserIdFromPasswordResetToken(string token) method do what you want.
More info here.
Update:
Sorry but I didn't saw that you were already using that method... So if you want get the username and you are using code first migrations of Entity Framework, you can get the username with the following LINQ expression:
string username = yourDbContext.UserProfiles.FirstOrDefault(up=>up.UserId == userId).Username;

Send email to user for password reset

The flow is:
user enters email address
after submit, an email is sent to the user
The email will include a link that will take the user to a reset password page.
Now, how do I fetch user's ID based on the email address and encrypt it? Then what should link be? Like, what I want is fetch the User ID then encrypt it somehow so that the link doesn't contain the actual ID and that link will take the user to a page that will have textboxes to reset the password. I am just confused how to go about it.
Also is this the secure way? To reset a password like this?
I usually create a new table in the database:
PasswordresetRequest with the following fields:
Id: Guid - Id of password reset request.
Accountid: string - username of user
Created: DataTime - timestamp of when password reset were created
Flow is as follows:
User request password reset at web site.
A new record is created in the PasswordresetRequest table.
An email with a link to the password reset page with the password request id as request parameter is sent to the user.
User click on link in email which send him to password reset page.
Password request if fetched from database from request parameter. If request could be found or and request is not older than e.g. 12 hours a form is presented to user where he can enter a new password.
This is pretty simple to implement and is secure enough for most sites.
There is any number of ways to go about doing this. If your major concern is security, one way could be to send a link that contains a guid parameter which you create and store on your end (in a db table, file or whatever suits you) together with the user id associated with it. When the request for password reset comes in, you check for the guid and look if there is one matching value in your db/file/whatever and proceed with the password reset. Don't forget to delete the guid from your storage to prevent multiple use of the same link.
There is a railscast on exactly this subject: http://railscasts.com/episodes/274-remember-me-reset-password?view=asciicast

Categories