WebSecurity.ConfirmAccount(Id) is always false - c#

Ive got a problem: my websecurity always throws false on confirmation. What am I doing wrong?
Here is my Validate Action(Ive debugged it, the id received is the right confirmation token:
public ActionResult Validate(String Id)
{
if (String.IsNullOrEmpty(Id))
{
return View();
}
bool b = WebSecurity.ConfirmAccount(Id);
if (b)
{
return View("ConfirmationSuccess");
}
return View("ConfirmationFailure");
}
And here is my registration action:
public ActionResult Register(RegisterModel model, string ReturnUrl)
{
if (ModelState.IsValid)
{
// Попытка зарегистрировать пользователя
try
{
string confirmationToken = WebSecurity.CreateUserAndAccount(model.rEmail.ToLower(), model.rPassword, null, true);
dynamic email = new Email("~/Views/Emails/RegisterConfirmation.cshtml");
email.To = model.rEmail;
email.ConfirmationToken = confirmationToken;
email.Send();
return RedirectToAction("EmailValidation", new { Email = model.rEmail.ToLower() });
}
catch (MembershipCreateUserException e)
{
string field = string.Empty;
switch (e.StatusCode)
{
case MembershipCreateStatus.DuplicateUserName:
field = "rEmail";
break;
case MembershipCreateStatus.InvalidPassword:
field = "rPassword";
break;
default:
field = "RegisterForm";
break;
}
ModelState.AddModelError(field, ErrorCodeToString(e.StatusCode));
}
}
ViewBag.RegisterModel = model;
ViewBag.ReturnUrl = ReturnUrl;
ViewBag.LoginModel = new LoginModel();
//ModelState.AddModelError("rEmail", "Пользователь с таким e-mail уже зарегистрирован");
// Появление этого сообщения означает наличие ошибки; повторное отображение формы
return View("Login");
}
after registration the email is sent, the link is the right link, but when It goes to WebSecurity.ConfirmAccount(Id), it always throws false....
Thank you for your time and sorry for my bad English.
UPD:
There is a converter for all urls to lower on my IIS server. Could it be the case, that it compares keys case-sensitive? And how can I fix this?
UPD:
Ok, the problem is really in lowercase url. WebSecurity.ConfirmAccount is case-sensitive... Ive made a little workaround in my action so that I could get the right ConfitmtionToken, but this is not fully right way, while there could be two identical ConfirmationToken.ToLower() as I think, So, please, somebody, point me the right way to do it.
And here is my workaround:
public ActionResult Validate(String Id)
{
if (String.IsNullOrEmpty(Id))
{
return View();
}
//bool b = WebSecurity.ConfirmAccount(Id);
using (var ctx = new DBContext())
{
Id = ctx.wpMembership.Where(s => s.ConfirmationToken.Equals(Id, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault().ConfirmationToken;
}
if (WebSecurity.ConfirmAccount(Id))
{
return View("ConfirmationSuccess");
}
return View("ConfirmationFailure");
}

This is because you are passing the account Id not the confirmation token. You need to pass the confirmation token.
The confirmation token is generated by the CreateAccount(String, String, Boolean), CreateUserAndAccount(String, String, Object, Boolean), and GeneratePasswordResetToken(String, Int32) methods. The token is ordinarily sent to the user as a link in an email message, and the user clicks the link in order to verify his or her identity.

Related

Pass HttpPostedFileBase object in asp.net Identity action method and validating with face api

Hello guys i try to implement a registration functionality using asp.net identity.
One of my required properties is the new user's photo which i pass as an argument in a action method where finally i call faceServiceClient.DetectAsync() method of azure face api.
I grab the photo using a <input type='file'> in my view
and then in account controller of identity in register action method i use a HttpPostedFileBase object to read it.
The problem is that faceServiceClient.DetectAsync() needs as first argument a stream object or a string(imagePath) but in my case i cant figure it out how i can give a stream object or the image path
HttpPostedFileBase doesn't retrieve the image path but to get the image i need this type of object.
And then even if i try to pass httpPostedFilebase object.InputStream as argument i get null by break pointing the await faceServiceClient.DetectAsync command line
To be specific
Account Controller
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register([Bind(Exclude = "UserPhoto")]RegisterViewModel model, HttpPostedFileBase userPhoto)
{
if ((userPhoto==null) || (userPhoto.ContentLength <= 0))
{
ModelState.AddModelError("error", #"Please Select a profile picture");
}
if (ModelState.IsValid)
{
var faceApiresult = await new FaceRecognitionController().GetDetectedFaces(userPhoto);
if (!faceApiresult)
{
ModelState.AddModelError("error", #"Your picture does not include your face");
return RedirectToAction("Index", "Home");
}
var user = new ApplicationUser
{
UserName = model.Email,
Email = model.Email,
UName = model.Username,
UserPhoto = model.UserPhoto
};
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 https://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);
}
and my FaceRecognitionController
public class FaceRecognitionController : Controller
{
private static string ServiceKey = ConfigurationManager.AppSettings["FaceServiceKey"];
private static string EndPoint = ConfigurationManager.AppSettings["FaceServiceEndPoint"];
[HttpGet]
public async Task<dynamic> GetDetectedFaces(HttpPostedFile userPhoto)
{
var photo = new byte[userPhoto.ContentLength];
if (userPhoto.ContentLength == 0) return false;
try
{
// Create Instance of Service Client by passing Servicekey as parameter in constructor
var faceServiceClient = new FaceServiceClient(ServiceKey);
var faces = await faceServiceClient.DetectAsync(userPhoto.InputStream, true, true, new FaceAttributeType[] { FaceAttributeType.Gender, FaceAttributeType.Age, FaceAttributeType.Smile, FaceAttributeType.Glasses });
//check if find any faces
var results = faces.Length;
return results != 0;
}
catch (FaceAPIException)
{
//do exception work
}
return false;
}
}
}
As you can see i just check if validation api find any face and just return true to complete the registration
any thoughts on how i can overcome this??
Ok after some more searching i found this article
https://learn.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads
in which i read that i can use this line of code
var filePath = Path.GetTempFileName();
and get the temp file path .So after that i successfully added it as a parameter to the action method and then in faceServiceClient.DetectAsync
and use tis path as first argument and worked

How to save previous password to be able to change current one

I'm working on an intranet, I fused the creation of an account (through form authentication) with the creation of an Employee - creating an Employee creates an account (simple).
But here's the issue, I wish to make it that when I change my password in the Employee section, it also changes it in the form auth. part.
Here's what I have so far :
[HttpPost]
public ActionResult Edit(Employee objToEdit, FormCollection form)
{
//string oldpwd = objToEdit.Password;
IEnumerable<SelectListItem> CompanyList = _service.ListCompany();
ViewBag.CompanyList = CompanyList;
IEnumerable<SelectListItem> SupervisorList = _service.ListSupervisor();
ViewBag.SupervisorList = SupervisorList;
objToEdit.UpdatedDate = System.DateTime.Now;
objToEdit.CompanyId = int.Parse(form["CompanyId"]);
objToEdit.Supervisor = form["Supervisor"];
if (_service.Edit(objToEdit))
{
//bool changePasswordSucceeded; // Find a way to store old and new pwd
//try
//{
// changePasswordSucceeded = WebSecurity.ChangePassword(User.Identity.Name, oldpwd, objToEdit.Password);
//}
//catch (Exception)
//{
// changePasswordSucceeded = false;
//}
//if (changePasswordSucceeded)
//{
// return RedirectToAction("Index", new { Message = CRAWebSiteMVC.Controllers.AccountController.ManageMessageId.ChangePasswordSuccess });
//}
//else
//{
// ModelState.AddModelError("", "The current password is incorrect or the new password is invalid.");
//}
return new RedirectResult(Url.Action("Index"));
}
return View();
}
Everything that is in comment comes from the form authentication Manage function, I wanted to take the old password and change it with the new, but I can't seem to find a way to save the old one.
Tested it with the password written directly in the oldpwd (e.g oldpwd = "mypwd") string and it worked, all I need is a way to save the previous one.
Any ideas how this can be achieved?
Have you tried to concatenate old pwd with new pwd ?
I mean:
oldpwd = "mypwd"."====".oldpwd;
With this oldpwd variable will look like this:
NEWPASS====OLDPASS
Best of luck !

Using UserManager.FindAsync with a custom UserStore

I have implemented a custom UserStore, it implements IUserStore<DatabaseLogin, int> and IUserPasswordStore<DatabaseLogin, int>.
My Login action method is as below:
if (ModelState.IsValid)
{
if (Authentication.Login(user.Username, user.Password))
{
DatabaseLogin x = await UserManager.FindAsync(user.Username, user.Password);
DatabaseLogin Login = Authentication.FindByName(user.Username);
if (Login != null)
{
ClaimsIdentity ident = await UserManager.CreateIdentityAsync(Login,
DefaultAuthenticationTypes.ApplicationCookie);
AuthManager.SignOut();
AuthManager.SignIn(new AuthenticationProperties
{
IsPersistent = false
}, ident);
return RedirectToAction("Index", "Home");
}
}
else
{
ModelState.AddModelError("", "Invalid Login");
}
}
return View();
In the custom authentication class that I wrote, Authentication, I have a Login method that works fine, also FindByName method returns an app user. But if I try to SignIn with that login, the user isn't recognized as authenticated and HttpContext.User.Identity is always null, so I imagine that I have to try UserManager.FindAsync.
This method calls FindByNameAsync and GetPasswordHashAsync, and it always return null.
public Task<DatabaseLogin> FindByNameAsync(string userName)
{
if (string.IsNullOrEmpty(userName))
throw new ArgumentNullException("userName");
return Task.FromResult<DatabaseLogin>(Authentication.FindByName(userName));
}
public Task<string> GetPasswordHashAsync(DatabaseLogin user)
{
if (user == null)
throw new ArgumentNullException("user");
return Task.FromResult<string>(user.Password);
}
And the Authentication.FindByName
public static DatabaseLogin FindByName(string name)
{
string GetUserQuery = string.Format(
"USE db;SELECT principal_id AS id, name as userName, create_date AS CreateDate, modify_date AS modifyDate FROM sys.database_principals WHERE type='S' AND authentication_type = 1 AND name = '{0}'"
, name);
DatabaseLogin user;
using (var db = new EFDbContext())
{
user = db.Database.SqlQuery<DatabaseLogin>(GetUserQuery).FirstOrDefault();
}
user.Password = Convert.ToBase64String(Encoding.ASCII.GetBytes("pass"));
return user;
}
As you can see I'm using database users, I'm not sure how I can retrieve a hashed password for them. For now, I'm just storing the Base65 of the correct password!
I have no idea where I'm going wrong, any guidance is welcome.
Short answer: nothing's wrong. User is authenticated in other action methods, but apparently not in the current action method.
This is the process that I followed, maybe it will help you debug your app.
After reading the source code, FindAsync first calls FindByNameAsync, followed by CheckPasswordAsync which references VerifyPasswordAsync. So it should be fine If I could override VerifyPasswordAsync.
I created a custom password hasher that implements IPasswordHasher, and registered it in the create method of my UserManager like this:
manager.PasswordHasher = new DbPasswordHasher();
So by now, I can get my user from UserManager.FindAsync, but it turned out that it doesn't matter where you get the user since HttpContext.User.Identity is still null! My mistake was that I didn't notice the user isn't authenticated in the current action, in other action methods it works as expected!

"Invalid token" error while reset password, ASP.Net Identity 2.2.0

I'm developing a MVC5 ASP.Net application.
I'm using Identity 2.2.0 for authentication.
Everything is OK, but I can't reset my password, because of Invalid Token error.
The following are ResetPassword related actions in Account controller.
[AllowAnonymous]
public ActionResult ForgotPassword()
{
return View();
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ForgotPassword(ForgotPasswordViewModel model)
{
if (!ModelState.IsValid) return View(model);
ApplicationUser userModel = await UserManager.FindByNameAsync(model.Email);
if (userModel == null)
ModelState.AddModelError("", "The user doesn't exist");
if (userModel != null && !await UserManager.IsEmailConfirmedAsync(userModel.Id))
ModelState.AddModelError("", "The user email isn't confirmed");
if (!ModelState.IsValid) return View();
var user = _userService.GetUserByEmail(model.Email);
// 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 websiteTitle = StaticAssests.WebsiteTitle;
string emailContext = _settingService.GetValue(SettingNames.ResetPasswordMailFormat);
string code = await UserManager.GeneratePasswordResetTokenAsync(userModel.Id);
string callbackUrl = Url.Action("ResetPassword", "Account", new { userId = userModel.Id, code }, Request.Url.Scheme);
emailContext = emailContext.Replace("{userfullname}", user.FullName);
emailContext = emailContext.Replace("{websitetitle}", websiteTitle);
emailContext = emailContext.Replace("{websitedomain}", StaticVariables.WebsiteDomain);
emailContext = emailContext.Replace("{username}", userModel.UserName);
emailContext = emailContext.Replace("{resetpasswordurl}", callbackUrl);
emailContext = emailContext.Replace("{date}", new PersianDateTime(DateTime.Now).ToLongDateTimeString());
await UserManager.SendEmailAsync(userModel.Id, string.Format("Reset password {0}", websiteTitle), emailContext);
return RedirectToAction("ForgotPasswordConfirmation", "Account");
}
[AllowAnonymous]
public ActionResult ForgotPasswordConfirmation()
{
return View();
}
[AllowAnonymous]
public ActionResult ResetPassword(string code)
{
return code == null ? View("Error") : View();
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ResetPassword(ResetPasswordViewModel model)
{
if (!ModelState.IsValid) return View(model);
ApplicationUser userModel = await UserManager.FindByNameAsync(model.Email);
if (userModel == null)
{
ModelState.AddModelError("", "The user doesn't exist");
return View();
}
// Invalid Token error
IdentityResult result = await UserManager.ResetPasswordAsync(userModel.Id, model.Code, model.Password);
if (result.Succeeded)
{
return RedirectToAction("ResetPasswordConfirmation", "Account");
}
AddErrors(result);
return View();
}
I've checked the followings:
1. Resetting email send successfully.
2. GeneratePasswordResetTokenAsync run without any problem. and the generated code with it is the same with code argument in ResetPassword action.
IdentityConfig:
public class ApplicationUserManager : UserManager<ApplicationUser, int>
{
public ApplicationUserManager(IUserStore<ApplicationUser, int> userStore) : base(userStore)
{
}
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
ApplicationUserManager applicationUserManager = new ApplicationUserManager(new ApplicationUserStore());
//ApplicationUserManager applicationUserManager = new ApplicationUserManager(context.Get<ApplicationUser>());
//new ApplicationUserManager(new UserStore<UserModel>(context.Get<ApplicationDbContext>()));
// Configure validation logic for usernames
//applicationUserManager.UserValidator = new UserValidator<UserIdentityModel, int>(applicationUserManager)
//{
// AllowOnlyAlphanumericUserNames = false,
// RequireUniqueEmail = true,
//};
applicationUserManager.PasswordValidator = new MyMinimumLengthValidator(6);
applicationUserManager.UserValidator = new MyUserModelValidator();
applicationUserManager.PasswordHasher = new MyPasswordHasher();
// Configure user lockout defaults
applicationUserManager.UserLockoutEnabledByDefault = true;
applicationUserManager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5);
applicationUserManager.MaxFailedAccessAttemptsBeforeLockout = 5;
// Register two factor authentication providers. This application uses Phone and Emails as a step of receiving a code for verifying the user
// You can write your own provider and plug in here.
applicationUserManager.RegisterTwoFactorProvider("PhoneCode",
new PhoneNumberTokenProvider<ApplicationUser, int>
{
MessageFormat = "Your security code is: {0}"
});
applicationUserManager.RegisterTwoFactorProvider("EmailCode",
new EmailTokenProvider<ApplicationUser, int>
{
Subject = "Security code",
BodyFormat = "your security code is {0}"
});
applicationUserManager.EmailService = new EmailService();
applicationUserManager.SmsService = new SmsService();
IDataProtectionProvider dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
applicationUserManager.UserTokenProvider =
new DataProtectorTokenProvider<ApplicationUser, int>(dataProtectionProvider.Create("ASP.NET Identity"));
}
return applicationUserManager;
}
}
What's wrong?
Update:
I've changed User Id from string to int.
I know this is an old question, but I've run into this on two projects and both times the issue was the exact same thing. Perhaps this answer might help others. The token being passed includes special characters that cannot be used as is within a URL string without causing problems.
When passing the token to the front end UI, be sure to URLEncode the token, something like this:
var callbackUrl =
new Uri(string.Format("{0}/resetpassword?userId={1}&code={2}",
ConfigurationManager.AppSettings["websiteUrl"], user.Id,
WebUtility.UrlEncode(token)));
When the token is passed back into the back end, Decode the token before passing it to the password ResetPassword function:
var result = await this.AppUserManager.ResetPasswordAsync(appUser.Id, WebUtility.UrlDecode(resetPasswordBindingModel.ConfirmCode),
resetPasswordBindingModel.NewPassword);
Both of the projects where I had this issue were running HTML/MVC/JavaScript on the front end and the validations were being done over ASP.NET WebAPI 2.x.
One other note: UrlDecode, for some reason, can't properly decode the plus symbol and you get a space in the token instead of the '+'. The trick I used is to just use a string replace to convert any spaces to + signs. It's not ideal, but I've not had a problem with it.

Change Password Controller not working - MVC

I've inherited a system which was written in MVC. This system uses the asp.net membership api, which works well. I've just discovered a bug however whereby the user is unable to change his/her password.
The system displays the form to enter in the old password, and the new password twice to confirm, however on clicking submit, it just redisplays the form, and does not change the password.
I've gone through the code but as I'm new enough to MVC, and using the membership api, I can't see anything overly wrong with it.
Here is the GET and POST code from within the Account Controller. If anyone could see anything wrong with this, I would greatly appreciate it. If anyone needs me to post additional information/code, please ask :)
Also, after debugging through the code, what seems to happen is, after
if (ModelState.IsValid)
is hit, the nested if statement within this is skipped and the code jumps straight down to the bottom to redisplay the form.
[Authorize]
public ActionResult ChangePassword(string source)
{
ViewData["PasswordLength"] = MembershipService.MinPasswordLength;
ViewData["source"] = source;
if (!string.IsNullOrEmpty(source))
{
return View("ChangePassword", source);
}
return View("ChangePassword", "User");
}
[Authorize]
[HttpPost]
public ActionResult ChangePassword(ChangePasswordModel model, FormCollection formValues)
{
string source = formValues["source"];
if (formValues["btnCancel"] != null)
{
RedirectToRouteResult result = null;
// The user has clicked cancel. Redirect back to source!
//
switch (source)
{
case "user":
result = RedirectToAction("Index", "ManageAccount", new { Area = "User" });
break;
case "administrator":
result = RedirectToAction("Index", "ManageAccount", new { Area = "Administrator" });
break;
}
return result;
}
if (ModelState.IsValid)
{
if (MembershipService.ChangePassword(User.Identity.Name, model.OldPassword, model.NewPassword))
{
return RedirectToAction("Index", "ManageAccount", new { Area = "User" });
}
else
{
ModelState.AddModelError("", "The current password is incorrect or the new password is invalid.");
}
}
// If we got this far, something failed, redisplay form
ViewData["PasswordLength"] = MembershipService.MinPasswordLength;
ViewData["source"] = source;
return View("ChangePassword", formValues["source"], model);
}
Maybe you could check for the Errors as stated here
ASP.NET MVC Model State

Categories

Resources