We've been running for ~4 months, and so far we haven't had any complaints. Today, we have somebody complain as creating an account isn't working for them. So I tried myself, and debugging I have indeed found an issue.
We're getting an error of
{"UserId not found."}
Which happens at this line
var userIdentity = manager.CreateIdentity(user, DefaultAuthenticationTypes.ApplicationCookie);
I can not understand why when I try to create an account it works perfectly, but using their email / name it always spits out errors... makes no sense to my inexperienced brain?
Here is my full code
public bool CreateUser(string userName, string Email, string Password, string fName, string lName, string country, string ip)
{
try
{
// Default UserStore constructor uses the default connection string named: DefaultConnection
var userStore = new UserStore<IdentityUser>();
var manager = new UserManager<IdentityUser>(userStore);
manager.PasswordValidator = new PasswordValidator
{
RequiredLength = 1,
RequireNonLetterOrDigit = false,
RequireDigit = false,
RequireLowercase = false,
RequireUppercase = false
};
var user = new IdentityUser() { UserName = userName, Email = Email, EmailConfirmed = false };
IdentityResult result = manager.Create(user, Password);
Utilities u = new Utilities();
var usersID = user.Id;
//u.SendMailConfirmation(Email, usersID, fName);
userDetails(usersID, ip, fName, lName, country);
var authenticationManager = HttpContext.Current.GetOwinContext().Authentication;
var userIdentity = manager.CreateIdentity(user, DefaultAuthenticationTypes.ApplicationCookie);
authenticationManager.SignIn(new AuthenticationProperties() { }, userIdentity);
return true;
}
catch (Exception ex)
{
return false;
}
}
And you are sure an user does not already exist with the same email?
Related
I am trying to create a user for a mixed authentication project. I am using MixedAuthExtension.cs.
I encounter a problem when I reach this line
IdentityResult result = await UserManager.CreateAsync(user,model.Password);
but the system user is created and the AspNetUser.
Thanks in advance for your help
The current code creates both the system user and ASP.NET user but it fails to sync identity
[ValidateAntiForgeryToken]
[HttpPost]
public async Task<ActionResult> CreateSystemUser(RegisterViewModel model, string key)
{
var _context = new RequestToFillDbContext();
#region Initialise
Initialise(_context);
var password = SecurityHelper.GeneratePassword();
UserStore<ApplicationUser> store = new UserStore<ApplicationUser>(_context);
UserManager<ApplicationUser> UserManager = new UserManager<ApplicationUser>(store);
UserManager.UserValidator = new UserValidator<ApplicationUser>(UserManager) { AllowOnlyAlphanumericUserNames = false };
model.Password = password;
model.ConfirmPassword = password;
ModelState.Clear();
#endregion
string serviveNumber = model.ServiceNumber;
if (!_context.SystemUsers.Any(s => s.EmailAddress == model.EmailAddress)
&& !_context.SystemUsers.Any(s => s.UserName == model.UserName))
{
if (ModelState.IsValid)
{
var emp = new RequestToFillApplicationHelper().GetEmployeeByServiceNumber(serviveNumber);
model.Designation = emp.Designation;
//TODO: Query Db for Id
//var identityManager = new IdentityManager();
var user = new ApplicationUser
{
UserName = model.UserName,
Email = model.EmailAddress,
EmailConfirmed = true,
SystemUser = new SystemUser()
{
FirstName = model.FirstName,
LastName = model.LastName,
UserName = model.UserName,
CompanyName = model.CompanyName,
Designation = model.Designation,
EmailAddress = model.EmailAddress,
IsTemporaryPassword = true,
//TempPasswordExpiryDateTime = DateTime.Now.AddHours(24),
SystemUserTypeId = model.SystemUserTypeId,
ServiceNumber = model.ServiceNumber,
IsActive = true,
IsDeleted = false,
IsLocked = false,
CreatedDateTime = DateTime.Now,
IsPasswordReset = false
}
};
//db.SaveChanges();
try
{
IdentityResult result = await UserManager.CreateAsync(user,model.Password);
//Assign user to role
I get a
"Property set method not found."Line 258
which is below
IdentityResult result = await UserManager.CreateAsync(user,model.Password);
I am trying to integrate AWS Cognito into a web site. I am trying to force a user to change their own password. I already had a method for voluntary password resets and I tried to use it for forced password reset. Here is the method:
internal async Task<bool> ResetPassword(string username, string oldPassword, string newPassword) {
AmazonCognitoIdentityProviderClient provider = new AmazonCognitoIdentityProviderClient(new Amazon.Runtime.AnonymousAWSCredentials());
CognitoUserPool userPool = new CognitoUserPool(CognitoHelper.POOL_ID, CognitoHelper.CLIENTAPP_ID, provider);
CognitoUser user = new CognitoUser(username, CognitoHelper.CLIENTAPP_ID, userPool, provider);
InitiateSrpAuthRequest authRequest = new InitiateSrpAuthRequest() {
Password = oldPassword
};
AuthFlowResponse authResponse = await user.StartWithSrpAuthAsync(authRequest).ConfigureAwait(false);
await user.ChangePasswordAsync(oldPassword, newPassword);
return true;
} // ResetPassword
When I call this method on a voluntary password reset, it works fine. On a forced password reset, the "StartWithSrpAuthAsync" throws an exception complaining "Password reset required for the user". No kidding - that is why I am trying to change the password.
The problem is that the "ChangePasswordAsync" method requires the user be authenticated before it is called. I can't authenticate the user because the password needs to be reset, but I can't change the password because the user needs to be authenticated first.
I tried a hack to solve my issue by catching the "Password reset required for the user" exception hoping the user was authenticated anyway. Unfortunately no luck:
internal async Task<bool> ResetPassword(string username, string oldPassword, string newPassword) {
AmazonCognitoIdentityProviderClient provider = new AmazonCognitoIdentityProviderClient(new Amazon.Runtime.AnonymousAWSCredentials());
CognitoUserPool userPool = new CognitoUserPool(CognitoHelper.POOL_ID, CognitoHelper.CLIENTAPP_ID, provider);
CognitoUser user = new CognitoUser(username, CognitoHelper.CLIENTAPP_ID, userPool, provider);
InitiateSrpAuthRequest authRequest = new InitiateSrpAuthRequest() {
Password = oldPassword
};
try {
AuthFlowResponse authResponse = await user.StartWithSrpAuthAsync(authRequest).ConfigureAwait(false);
await user.ChangePasswordAsync(oldPassword, newPassword);
} catch (Exception exp) {
if (exp.Message == "Password reset required for the user") {
await user.ChangePasswordAsync(oldPassword, newPassword);
} else {
throw exp;
} // if else
} // try catch
return true;
} // ResetPassword
Any thoughts?
I tried treating a forced reset like a forgot password case and it worked! Specifically to send a new verification code to the user's email:
internal async Task<ForgotPasswordResponse> ForgotPassword(string username) {
ForgotPasswordRequest forgotPasswordRequest = new ForgotPasswordRequest();
forgotPasswordRequest.Username = username;
forgotPasswordRequest.ClientId = CLIENTAPP_ID;
ForgotPasswordResponse forgotPasswordResponse = await provider.ForgotPasswordAsync(forgotPasswordRequest).ConfigureAwait(false);
return forgotPasswordResponse;
} // ForgotPassword
and:
internal async Task<ConfirmForgotPasswordResponse> ConfirmForgotPassword(string validationCode, string username, string newPassword) {
ConfirmForgotPasswordRequest confirmForgotPasswordRequest = new ConfirmForgotPasswordRequest();
confirmForgotPasswordRequest.Username = username;
confirmForgotPasswordRequest.ClientId = CLIENTAPP_ID;
confirmForgotPasswordRequest.Password = newPassword;
confirmForgotPasswordRequest.ConfirmationCode = validationCode;
ConfirmForgotPasswordResponse confirmForgotPasswordResponse = await provider.ConfirmForgotPasswordAsync(confirmForgotPasswordRequest).ConfigureAwait(false);
return confirmForgotPasswordResponse;
} // ConfirmForgotPassword
to "reset" the new password. From what I can see in the documentation, this is not spelled out anywhere.
I tried your code (in the first question) and got the same error
Changing the provider declaration as follows fixed it
static Amazon.RegionEndpoint region = Amazon.RegionEndpoint.APSoutheast2;
AmazonCognitoIdentityProviderClient provider = new AmazonCognitoIdentityProviderClient(new Amazon.Runtime.AnonymousAWSCredentials(), region);
Of course the region should be changed to whatever region you are on
I'm trying to add a user programmatically to ASP.NET MVC Identity.
The error I'm encountering is: UserManager threw an exception of type 'System.NullReferenceException'
This function is called through a POST not originating from this site. It sits right below public async Task<ActionResult> Register(RegisterViewModel model) in the AccountController.
[AllowAnonymous]
public async Task<bool> GenerateUser(string email)
{
var user = new ApplicationUser { UserName = email, Email = email };
string password = System.Web.Security.Membership.GeneratePassword(12, 4);
var result = await UserManager.CreateAsync(user, password);
if (result.Succeeded)
{
// Omitted
}
else { AddErrors(result); }
return true;
}
I have also attempted to use the below code to perform the same action, but I get the error that special characters can't be in the UserName (I am using an email address), but this is definitely allowed as it's how all my users are created using public async Task<ActionResult> Register(RegisterViewModel model).
string password = System.Web.Security.Membership.GeneratePassword(12, 4);
var store = new Microsoft.AspNet.Identity.EntityFramework.UserStore<ApplicationUser>();
var manager = new ApplicationUserManager(store);
var user = new ApplicationUser() { Email = email, UserName = email };
var result = manager.Create(user, password);
The user object is the same as if I had filled a form to create a new user on the site (using public async Task<ActionResult> Register(RegisterViewModel model)), and the password is just a string, also the same.
public async Task<ActionResult> Register(RegisterViewModel model) is as per the scaffolded default but here it is below anyway for reference:
// POST: /Account/Register
[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 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");
// TODO: Email Sent
return View("ConfirmationSent");
}
AddErrors(result);
}
// If we got this far, something failed, redisplay form
return View(model);
}
Edit:
I call the function with:
var result = new AccountController().GenerateUser(model.emailAddress);
Edit2:
As asked for: This is the class definition for the ApplicationUserManager
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(context.Get<ApplicationDbContext>()));
// Configure validation logic for usernames
manager.UserValidator = new UserValidator<ApplicationUser>(manager)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = true
};
// Configure validation logic for passwords
manager.PasswordValidator = new PasswordValidator
{
RequiredLength = 8,
RequireNonLetterOrDigit = false,
RequireDigit = false,
RequireLowercase = false,
RequireUppercase = false,
};
// Configure user lockout defaults
manager.UserLockoutEnabledByDefault = true;
manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5);
manager.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 it in here.
manager.RegisterTwoFactorProvider("Phone Code", new PhoneNumberTokenProvider<ApplicationUser>
{
MessageFormat = "Your security code is {0}"
});
manager.RegisterTwoFactorProvider("Email Code", new EmailTokenProvider<ApplicationUser>
{
Subject = "Security Code",
BodyFormat = "Your security code is {0}"
});
manager.EmailService = new EmailService();
manager.SmsService = new SmsService();
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
manager.UserTokenProvider =
new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"));
}
return manager;
}
}
The issue is with the UserManager, this solves the issue.
ApplicationDbContext context = new ApplicationDbContext();
var roleManager = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(context));
var UserManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(context));
UserManager.UserValidator = new UserValidator<ApplicationUser>(UserManager)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = true
};
string password = System.Web.Security.Membership.GeneratePassword(12, 4);
var user = new ApplicationUser();
user.Email = model.Email;
user.UserName = model.Email;
string userPWD = password;
var result = UserManager.Create(user, userPWD);
I want to save something inside my 'Identity' generated cookie. I'm currently using the default Identity setup from the Docs.
Startup.cs
services.Configure<IdentityOptions>(options =>
{
// User settings
options.User.RequireUniqueEmail = true;
// Cookie settings
options.Cookies.ApplicationCookie.AuthenticationScheme = "Cookies";
options.Cookies.ApplicationCookie.ExpireTimeSpan = TimeSpan.FromHours(1);
options.Cookies.ApplicationCookie.SlidingExpiration = true;
options.Cookies.ApplicationCookie.AutomaticAuthenticate = true;
options.Cookies.ApplicationCookie.LoginPath = "/Account";
options.Cookies.ApplicationCookie.LogoutPath = "/Account/Logout";
});
AccountController.cs
var result = await _signInManager.PasswordSignInAsync(user.UserName, model.Password, true, true);
if (result.Succeeded)
{
_logger.LogInformation(1, "User logged in.");
var tokens = new List<AuthenticationToken>
{
new AuthenticationToken {Name = "Test", Value = "Test"},
};
var info = await HttpContext.Authentication.GetAuthenticateInfoAsync("Cookies");
info.Properties.StoreTokens(tokens);
It seems this doesn't work. Because the cookie isn't created yet. The 'Info' variable is empty.
I could solve it by using the 'CookieMiddleware'
Startup.cs
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationScheme = "Cookies",
ExpireTimeSpan = TimeSpan.FromHours(1),
SlidingExpiration = true,
AutomaticAuthenticate = true,
LoginPath = "/Account",
LogoutPath = "/Account/Logout",
});
But than I need to use
await HttpContext.Authentication.SignInAsync("Cookies", <userPrincipal>);
In this case I need to build myself a 'user principal'. And I prefer to leverage 'Identity' for this matter.
So is it possible to combine this?
If this is not the case how do I generate the claimsprincipal on a good way.
Without the need to 'map' every claim.
List<Claim> userClaims = new List<Claim>
{
new Claim("UserId", Convert.ToString(user.Id)),
new Claim(ClaimTypes.Name, user.UserName),
// TODO: Foreach over roles
};
ClaimsPrincipal principal = new ClaimsPrincipal(new ClaimsIdentity(userClaims));
await HttpContext.Authentication.SignInAsync("Cookies", principal);
So something like:
ClaimsPrincipal pricipal = new ClaimsPrincipal(user.Claims);
This doesn't work because user.Claims is of type IdentityUserClaim and not of type Security.Claims.Claim.
Thanks for reading.
Have a good day,
Sincerely, Brecht
I managed to solve my problem.
I wrote the same functionality that is inside the 'signInManager'. But adding my own authentication property.
var result = await _signInManager.PasswordSignInAsync(user, model.Password, true, true);
if (result.Succeeded)
{
await AddTokensToCookie(user, model.Password);
return RedirectToLocal(returnUrl);
}
if (result.RequiresTwoFactor)
{
// Ommitted
}
if (result.IsLockedOut)
{
// Ommitted
}
Code that actually saves something (tokens) inside the cookie:
private async Task AddTokensToCookie(ApplicationUser user, string password)
{
// Retrieve access_token & refresh_token
var disco = await DiscoveryClient.GetAsync(Environment.GetEnvironmentVariable("AUTHORITY_SERVER") ?? "http://localhost:5000");
if (disco.IsError)
{
_logger.LogError(disco.Error);
throw disco.Exception;
}
var tokenClient = new TokenClient(disco.TokenEndpoint, "client", "secret");
var tokenResponse = await tokenClient.RequestResourceOwnerPasswordAsync(user.Email, password, "offline_access api1");
var tokens = new List<AuthenticationToken>
{
new AuthenticationToken {Name = OpenIdConnectParameterNames.AccessToken, Value = tokenResponse.AccessToken},
new AuthenticationToken {Name = OpenIdConnectParameterNames.RefreshToken, Value = tokenResponse.RefreshToken}
};
var expiresAt = DateTime.UtcNow + TimeSpan.FromSeconds(tokenResponse.ExpiresIn);
tokens.Add(new AuthenticationToken
{
Name = "expires_at",
Value = expiresAt.ToString("o", CultureInfo.InvariantCulture)
});
// Store tokens in cookie
var prop = new AuthenticationProperties();
prop.StoreTokens(tokens);
prop.IsPersistent = true; // Remember me
await _signInManager.SignInAsync(user, prop);
}
The last 4 lines of code are the most important ones.
I am trying to implement some basic security for logins on a development website. I am using this code (C#) from https://crackstation.net/hashing-security.htm
For some reason it just doesn't work, the password hash generated is stored in my DB exactly as generated, it just doesn't seem to work when I try to validate a password against the hash using ValidatePassword(string password, string correctHash)
The ValidatePassword is supposed to generate the same hash, but it doesn't.
Does anybody have any experience using this code, or have any code that actually works? I have tried several now, including the one on the MSDN and none seem to fit the bill. It is suggested I shouldn't try to write my own code to achieve this.
Here is my code that calls the method, I have validated during debugging that the inputs are correct.
I have copied the code from the linked website into a static helper class which is called by my service methods below.
public ResultModel Register(string emailAddress, string password, string givenName, string familyName)
{
var _db = new EntityConnection();
if (_db.Users.Any(x => x.isDeleted == false && x.EmailAddress == emailAddress))
return new ResultModel() { success = false, message = "Email Address already registered, please attempt to Login" };
password = HashHelper.CreateHash(password);
var user = new User()
{
EmailAddress = emailAddress,
Password = password,
GivenName = givenName,
FamilyName = familyName,
DateAdded = DateTime.Now,
isDeleted = false,
isApproved = false
};
_db.Users.Add(user);
_db.SaveChanges();
var activationToken = new Token()
{
TokenType = "Account Activation",
User = user,
UserID = user.UserID,
DateAdded = DateTime.Now,
TokenCode = Guid.NewGuid().ToString()
};
_db.Tokens.Add(activationToken);
_db.SaveChanges();
return new ResultModel() { success = true, entity = user };
}
public ResultModel Login(string emailAddress, string password)
{
var _db = new EntityConnection();
var user = _db.Users.Where(x => x.isDeleted == false && x.EmailAddress == emailAddress);
if (!user.Any() || user.Count() > 1)
return new ResultModel() { success = false, message = "Credentials supplied do not match an Account, please try again." };
var existingHash = user.First().Password;
var result = HashHelper.ValidatePassword(password, existingHash);
if (result)
return new ResultModel() { success = true, entity = user.First() };
else
return new ResultModel() { success = false, message = "Credentials supplied do not match an Account, please try again." };
}