I created MVC 4 application.
In that application
If user forgot the password , I have method to send an email to user
to reset password.
If Admin want to change user current password ,I have method to send an email to user with relevant details.
So I'm getting same error when I try to send email
I'm getting errors like following
Error that I'm getting for Forgot Password method
Error that I'm getting for Edit User method
Seems like I'm having trouble when I try to send email , I'm using asp.net Identity membership
This is relevant code snippet for Forgot Password Method
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ForgotPassword(ForgotPasswordViewModel model)
{
if(ModelState.IsValid)
{
var username = await UserManager.FindByNameAsync(model.UserName);
var user = await UserManager.FindByEmailAsync(model.Email);
if (user != null && username != null)
{
var provider = new Microsoft.Owin.Security.DataProtection.DpapiDataProtectionProvider("My_Application");
UserManager.UserTokenProvider = new Microsoft.AspNet.Identity.Owin.DataProtectorTokenProvider<ApplicationUser>(provider.Create("EmailConfirmation"));
var code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
System.Net.Mail.MailMessage m = new System.Net.Mail.MailMessage(
........
This is relevant code snippet for Edit User Method
[HttpPost]
[CustomAuthorization(IdentityRoles = "Admin")]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Edit_User(EditUserViewModel editUser)
{
try
{
if (ModelState.IsValid)
{
AspNetUser user = db.AspNetUsers.Find(editUser.Id);
if(editUser.Change == "Yes"){
String userId = editUser.Id;
String newPassword = editUser.NewPassword;
var provider = new Microsoft.Owin.Security.DataProtection.DpapiDataProtectionProvider("My_Application");
UserManager.UserTokenProvider = new Microsoft.AspNet.Identity.Owin.DataProtectorTokenProvider<ApplicationUser>(provider.Create("EmailConfirmation"));
var code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
System.Net.Mail.MailMessage m = new System.Net.Mail.MailMessage(
................................................
Seems like having problem in same spot, but couldn't figure it out yet
I had same issue , then after many research I found out that problem is in IIS deployment
so following this thread I able to fix my issue
The data protection operation was unsuccessful
Open your IIS Manager
Find out what AppPool your application is using by selecting your App, right-click on it, and Select Manage Application -> Advanced
Settings.
After that, on the top left hand side, select Applications Pools,and go ahead and select the App Pool used by your app.
Right-click on it, and select Advanced Settings, Go to the Process Model Section and Find the "Load User Profile" Option and set it to
true.
i see
if (user != null && username != null)
are you trying to set those in the constructor? If so, you can't, you need to set them in the method.
You receive error code you wrote to the wrong place.
var provider = new Microsoft.Owin.Security.DataProtection.DpapiDataProtectionProvider("My_Application");
UserManager.UserTokenProvider = new Microsoft.AspNet.Identity.Owin.DataProtectorTokenProvider<ApplicationUser>(provider.Create("EmailConfirmation"));
you should write to Startup.Auth class. lie this :
app.CreatePerOwinContext(IdentityFactory.CreateContext);
app.CreatePerOwinContext<CustomUserManager>(IdentityFactory.CreateUserManager);
User Manager Definitions and Settings
public static CustomUserManager CreateUserManager(IdentityFactoryOptions<CustomUserManager> options, IOwinContext context)
{
var manager = new CustomUserManager(new CustomUserStore(context.Get<CustomIdentityDbContext>()));
manager.UserValidator = new UserValidator<CustomUser, int>(manager)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = true
};
manager.UserLockoutEnabledByDefault = true;
manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(10);
manager.MaxFailedAccessAttemptsBeforeLockout = 5;
manager.PasswordValidator = new PasswordValidator
{
RequiredLength = 6,
RequireNonLetterOrDigit = true,
RequireDigit = true,
RequireLowercase = true,
RequireUppercase = true,
};
manager.EmailService = new IdentityEmailService();
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
manager.UserTokenProvider = new DataProtectorTokenProvider<CustomUser, int>(dataProtectionProvider.Create("My_Application"))
{
TokenLifespan = TimeSpan.FromHours(2)
};
}
return manager;
}
Important :You gotta be careful here
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
manager.UserTokenProvider = new DataProtectorTokenProvider<CustomUser, int>(dataProtectionProvider.Create("FocusOnStoreService"))
{
TokenLifespan = TimeSpan.FromHours(2)
};
}
Related
Long story short, I use Identity and in my solution I created a custom account settings page which works fine and dandy. The problem is that I have the users FirstName and LastName in the _Layout.cshtml. The name is set by a custom helper method I have:
public static MvcHtmlString GetUsersFirstAndLastName(this HtmlHelper helper)
{
string fullName = HttpContext.Current?.User?.Identity?.Name ?? string.Empty;
var userIdentity = (ClaimsPrincipal)Thread.CurrentPrincipal;
var nameClaim = identity?.FindFirst("fullname");
if (nameClaim != null)
{
fullName = nameClaim.Value;
}
return MvcHtmlString.Create(fullName);
}
This method works great, until a user goes to their profile and updates their name. If they change their name from George to Bob then when they go around on my website this method still pulls their name as George until they log out and log back in.
So what I did to fix that was when they update their name in the account settings I added some code to remove their old fullName claim, and add the new one, like this:
var identity = User.Identity as ClaimsIdentity;
// check for existing claim and remove it
var currentClaim = identity.FindFirst("fullName");
if (currentClaim != null)
identity.RemoveClaim(existingClaim);
// add new claim
var fullName = user.FirstName + " " + user.LastName;
identity.AddClaim(new Claim("fullName", fullName));
With this bit of code the _Layout view now updates the name (in our previous example George will now change to Bob). However, the moment the click out of that view to another place on the website or the moment they refresh the page it changes right back to George.
Still being a bit new to identity I'm a bit puzzled why this new updated claim does not work after they click around to a different page or refresh. Any help is appreciated. :)
When adding the new claim you also needed to do this:
var authenticationManager = HttpContext.GetOwinContext().Authentication;
authenticationManager.AuthenticationResponseGrant = new AuthenticationResponseGrant(new ClaimsPrincipal(identity), new AuthenticationProperties() { IsPersistent = true });
So the new full code block is:
public static MvcHtmlString GetUsersFirstAndLastName(this HtmlHelper helper)
{
string fullName = HttpContext.Current?.User?.Identity?.Name ?? string.Empty;
var userIdentity = (ClaimsPrincipal)Thread.CurrentPrincipal;
var nameClaim = identity?.FindFirst("fullname");
var authenticationManager = HttpContext.GetOwinContext().Authentication;
authenticationManager.AuthenticationResponseGrant = new AuthenticationResponseGrant(new ClaimsPrincipal(identity), new AuthenticationProperties() { IsPersistent = true });
if (nameClaim != null)
{
fullName = nameClaim.Value;
}
return MvcHtmlString.Create(fullName);
}
I have a scheduled WebJob that runs on daily basis and checks the password expiry date for all users in my database. If the password expiry date is today, it will generate a password reset token and send it to the user via email. Then user clicks the url in the email and is brought to a website, where they input the new password.
I managed to generate a token in my WebJob and send it over via email. However, when resetting the password through my Asp.NET Website I get Invalid Token. I cannot figure out why. I assume it must have something to do with the token provider from my WebJob.
1) My Asp.NET website. The custom UserManager:
public class CustomUserManager : UserManager<ApplicationUser> {
public CustomUserManager(IUserStore<ApplicationUser> store) : base(store) { }
public static CustomUserManager Create(IdentityFactoryOptions<CustomUserManager> options, IOwinContext context) {
var db = context.Get<DataContext>();
var manager = new CustomUserManager(new UserStore<ApplicationUser>(db));
// [...]
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null) {
manager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"));
}
// [...]
return manager;
}
}
Which is used like this:
userManager = HttpContext.GetOwinContext().Get<CustomUserManager>();
// [...]
await userManager.ResetPasswordAsync(model.Id, model.Token, model.ConfirmPassword); // token here is invalid (although the string looks like a proper token)
2) My WebJob function:
public static async void CheckPasswords([QueueTrigger("checkpasswords")] string message) {
using (var db = new DataContext())
using (var userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(db))) {
var provider = new DpapiDataProtectionProvider("MyApp");
userManager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(provider.Create("PasswordReset"));
var users = await queryHandler.Run(new UserPasswordExpiryQuery());
foreach (var user in users) {
var days = new DateCalculations().DaysFromNow(user.PasswordExpiryDate);
// if password expired today
if (days == 0) {
var token = await userManager.GeneratePasswordResetTokenAsync(user.Id);
var url = string.Format("{0}/resetpass?user={1}&token={2}", settings.BaseUrl, user.Id, HttpUtility.UrlEncode(token));
// [...] send email logic here
}
}
}
}
LATER EDIT
I think I might have figured it out. I replaced the token provider in my Asp.NET app:
Old code:
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null) {
manager.UserTokenProvider =
new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"));
}
New code:
var provider = new DpapiDataProtectionProvider("MyApp");
manager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(provider.Create("ASP.NET Identity"));
Will do some further testing later on.
It's possible that the logic you are running is running against some sandbox limitation.
If you share your web app name, either directly or indirectly, and the UTC time of one such failure, I could potentially confirm this.
I'm testing some code which needs user to be logged in. When I'm trying to log in with AccountController, it's looks like everything is working, but at AccountController (IPrincipal) User is still null. How can I properly log in (or better, can I mock it somehow)?
public async Task SetupAsync()
{
var context = new DataContext();
var manager = new UserManager(new UserStore(context));
var accountController = new AccountController(manager);
var mockAuthenticationManager = new Mock<IAuthenticationManager>();
mockAuthenticationManager.Setup(am => am.SignOut());
mockAuthenticationManager.Setup(am => am.SignIn());
accountController.AuthenticationManager = mockAuthenticationManager.Object;
var user = new LoginViewModel
{
Email = "user#wp.pl",
Password = "useruser",
RememberMe = false
};
if (manager.FindByEmail("user#wp.pl") == null)
{
await manager.CreateAsync(new User { Email = "user#wp.pl", UserName = "user#wp.pl" }, "useruser");
}
await accountController.Login(user, "home/index");
_calendarController = new CalendarController(context);
}
Here I got User null exception:
public ClaimsPrincipal CurrentUser
{
get { return new ClaimsPrincipal((System.Security.Claims.ClaimsPrincipal)this.User); }
}
Edit: At return line, I have still User property null. This is sample from AccountController:
var user = await _userManager.FindAsync(model.Email, model.Password);
if (user != null)
{
await SignInAsync(user, model.RememberMe);
return RedirectToAction("index", "calendar");
}
You should mock your _userManager, and use a mock setup for when the method FindAsync is called. Then you return a fake user you can use later in the code
Figured it out on my own, probably not elegant solution but I'm happy anyway. #andreasnico your answer helped, thanks.
I'm mocking my custom ClaimsPrincipal, and setting up UserId - that's what I really needed.
var mockCp = new Mock<IClaimsPrincipal>();
mockCp.SetupGet(cp => cp.UserId).Returns(user.Id);
_calendarController.CurrentUser = mockCp.Object;
Can someone help me with the following code? I'm getting an error on this line and don't understand why:
string code = await UserManager.GeneratePasswordResetTokenAsync(user.Id);
Full code:
var user = await UserManager.FindByEmailAsync(model.Email);//Find user by email entered
if (user == null)
{
return View("ForgotPasswordConfirmation");
}
string code = await UserManager.GeneratePasswordResetTokenAsync(user.Id);
var callbackUrl = Url.Action("ResetPassword", "Login", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
The GeneratePasswordResetTokenAsync method requires a UserTokenProvider to be set in your UserManager.
You are receiving the error because of a null check in the GenerateUserTokenAsync called by your GeneratePasswordResetTokenAsync method.
A complementary to Scott Brady's answer. You should create the UserTokenProvider manually:
// db is of type DbContext or IdentityDbContext
var userManager = new UserManager(new UserStore(db));
var dataProtectionProvider = new DpapiDataProtectionProvider("Test");
userManager.UserTokenProvider = new DataProtectorTokenProvider<User, Guid>(dataProtectionProvider.Create("ASP.NET Identity"));
var user = await UserManager.FindByEmailAsync(model.Email);//Find user by email entered
//rest of the code
I am struggling a bit with Asp.net Identity and updating a user. When I run the code below succeed is false and the error message is that the user with username exists. Which is - of course - obvious because you are updating a user, not creating a new one.
I have tried to remove the username without much success, I was then told that the Name (I believe it meant Username) could not be empty.
Snip of code below.
public async Task<ActionResult> Edit(RegisterViewModel model)
{
var user = new User()
{
UserName = model.UserName, FirstName = model.FirstName, LastName = model.LastName, Email = model.EmailAddress,
ApplicationId = Utilities.ApplicationUtilities.GetApplicationId()
};
var userContext = new ApplicationDbContext();
var userStore = new UserStore<User>(userContext);
var userManager = new UserManager<User>(userStore);
var result = await userManager.UpdateAsync(user);
if (result.Succeeded)
{
var selectedRole = model.SelectedRole;
if (!userManager.IsInRole(user.Id, selectedRole.Id))
{
// We are removing the user from the old role. He / she cannot have two or more roles
userManager.RemoveFromRole(user.Id, model.OldRole);
// Now we are adding the user to the new role
userManager.AddToRole(user.Id, selectedRole.Id);
userManager.Update(user);
}
userContext.SaveChanges();
// await SignInAsync(user, isPersistent: false);
return RedirectToAction("Index", "UserManager");
}
The solution based on the input from Jonesy became something like this:
/*
* Some more information /Just in case someone else does the same
* mistakes I did...
*/
model.OldRole = "User"; // Name of old role - not ID of old role
model.SelectedRoleId = "Administrator"; // Name of new role, not ID of new role
// This test is here just to check if the model is valid or not
// By adding this part, you can check what is possibly wrong with your model
if (!ModelState.IsValid)
{
var errors = ModelState
.Where(x => x.Value.Errors.Count > 0)
.Select(x => new {x.Key, x.Value.Errors})
.ToArray();
}
// Creating the ApplicationDbContext object
var userContext = new ApplicationDbContext();
// Getting the list of users (I tried using Find here, but got errors)
var userList = userContext.Users.ToList();
// Decided to use First or Default. You also have to use double
// equal-characters(=) otherwise you will get errors
var user = userList.FirstOrDefault(u => u.UserName == model.UserName);
// Checking that we really found the user to update
if (user != null)
{
// populate the user object
user.UserId = model.UserId;
user.FirstName = model.FirstName;
user.LastName = model.LastName;
user.Email = model.EmailAddress;
}
// creating the UserStore object
var userStore = new UserStore<User>(userContext);
// ... and the userManager object
var userManager = new UserManager<User>(userStore);
// Do the update - I believe this is on the userManager-object
// and not in the database
var result = await userManager.UpdateAsync(user);
// If we get an error, we return to list of Users
// (You should log the error and also return the user to the form)
if (!result.Succeeded) return RedirectToAction("Index", "UserManager");
// Do the actual update in the database
userContext.SaveChanges();
// If the old role and the selected role is the same, we don't
// have to update
if (model.OldRole == model.SelectedRoleId) return RedirectToAction("Index", "UserManager");
// Get the selected role (sort of not needed, but here for clarity)
string selectedRole = model.SelectedRoleId;
// We are removing the user from the old role.
// In our application a user cannot have two or more roles
userManager.RemoveFromRole<User, string>(user.UserId, model.OldRole);
// Now we are adding the user to the new role
userManager.AddToRole<User, string>(user.UserId, selectedRole);
// We are updating the userManager-object
userManager.Update(user);
// And storing the information in the database
userContext.SaveChanges();
// Returning the user to the list of users
return RedirectToAction("Index", "UserManager");
use your dbContext to pull the user to update, instead of creating a new one:
var user = userContext.Find(model.UserName);
or you may need
var user = userContext.FirstOrDefault(u => u.UserName = model.UserName && u.Email = model.EmailAddress);
if(user != null)
{
//update user
}
this is an old one but just wanted to post my solution to the same update issue
var user = UserManager.FindByEmail(model.Email);
user.Address = model.Address;
user.City = model.City;
user.State = model.State;
var result = await UserManager.UpdateAsync(user);
you can also manage roles
user.Roles.Add(Role);
user.Roles.Remove(Role);