I am using asp.Identity to do the users and roles module in my application. I create user like this
var user = new ApplicationUser() { UserName = name, Email = email };
IdentityResult result1 = ApplicationUserManager.AppUserManager.Create(user, password);
It creates the user, the issue is that in the Application Manager it doesn't check for duplicate email. My application manager looks like this
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
var manager = new ApplicationUserManager(new EntityUserStore<ApplicationUser, Account, ApplicationRole, Role>());
AppUserManager = manager;
// 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 = 6,
RequireNonLetterOrDigit = false,
RequireDigit = false,
RequireLowercase = false,
RequireUppercase = false,
};
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
manager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"));
}
return manager;
}
The other is issue that, if I login using user name it works but if I use emal it returns null.
ApplicationUser user = UserManager.FindByEmail(email); // this returns null
Anyone familiar with this issue?
Your ApplicationUserManager.AppUserManager.Create does not validate the email because you are not referring to the ApplicationUserManager context,which is something like this:
var manager = Context.GetOwinContext().GetUserManager<ApplicationUserManager>();
var signInManager = Context.GetOwinContext().Get<ApplicationSignInManager>();
var user = new ApplicationUser() { UserName = name, Email = email };
IdentityResult result = manager.Create(user, password);
if (result.Succeeded)
The above example var manager will contain the ApplicationUserManager context and validation of email will be done by RequireUniqueEmail = true.
Related
what i am testing.
This is an identity server project with a login to federated gateway. I do not control this gateway and am having issues with them not returning the proper claims back to me that i need to verify the users logins. I would like to be able to test that i can handle these errors.
For example email claim is missing without that i can not login a user.
I have created a test that tests the email claim is missing returns an error.(Works fine)
Now I am trying to test the other side of things. If the claims are in fact there it should return the user that matches to the claims returned.
The method we are testing
public static async Task<(ApplicationUser user, string provider, string providerUserUserName, IEnumerable<Claim> claims, string message)> FindUserFromExternalProvider(AuthenticateResult result, UserManager<ApplicationUser> userManager, ILogger<SegesExternalController> logger)
{
var externalUser = result.Principal;
// try to determine the unique id of the external user (issued by the provider)
var eMailClaim = externalUser.FindFirst(SegesSettingsConstants.SegesEmailClaimName);
if(eMailClaim == null) return (null, null, null, null, $"{SegesSettingsConstants.SegesEmailClaimName} claim not found.");
// remove the user id claim so we don't include it as an extra claim if/when we provision the user
var claims = externalUser.Claims.ToList();
claims.LogSegesClaims(logger);
claims.Remove(eMailClaim);
// Should we remove more claims
var provider = result.Properties.Items["scheme"];
var providerUserUserName = eMailClaim.Value;
var user = await userManager.FindByEmailAsync(providerUserUserName); // Test Breaks here
return (user, provider, providerUserUserName, claims, null);
}
Test
[Fact]
public async void Federated_login_with_email_claim_return_no_error()
{
// Arrange
var principal = new ClaimsPrincipal();
principal.AddIdentity(new ClaimsIdentity(
new Claim[] {
new Claim("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", "Testbruger til André"),
new Claim("http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname", #"PROD\Salg43"),
new Claim("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/postalcode", "8200"),
new Claim("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/locality", "Aarhus N"),
new Claim("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress", "test#email.com"),
},
"FakeScheme"));
var authenticateResult = AuthenticateResult.Success(new AuthenticationTicket(principal, new AuthenticationProperties() { Items = { { "scheme", "fed" } } }, "FakeScheme"));
var exprectUser = new ApplicationUser()
{
UserName = "test#email.com",
NormalizedUserName = "TEST#EMAIL.COM",
NormalizedEmail = "TEST#EMAIL.COM",
Email = "test#email.com",
Id = 123,
EmailConfirmed = true
};
var mockEmailStore = new Mock<IUserEmailStore<ApplicationUser>>();
var mockQueryableUserStore = new Mock<IQueryableUserStore<ApplicationUser>>();
var mockUserStore = new Mock<IUserStore<ApplicationUser>>();
mockUserStore.Setup(x => x.FindByIdAsync(exprectUser.Id.ToString(), CancellationToken.None)).ReturnsAsync(exprectUser);
var userManager = new UserManager<ApplicationUser>(mockUserStore.Object, null, null, null, null, null, null, null, null);
var logger = new Logger<ExternalController>(new LoggerFactory());
// Act
var (user, provider, providerUserUserName, claims, errorMessage) = await AuthorizationHelpers.FindUserFromExternalProvider(authenticateResult, userManager, logger);
// Assert
user.ShouldNotBeNull();
}
The issue with above.
I am trying to moq a usermanager for my unit test
var exprectUser = new ApplicationUser()
{
UserName = "test#email.com",
NormalizedUserName = "TEST#EMAIL.COM",
NormalizedEmail = "TEST#EMAIL.COM",
Email = "test#email.com",
Id = 123,
EmailConfirmed = true
};
var mockUserStore = new Mock<IUserStore<ApplicationUser>>();
mockUserStore.Setup(x => x.FindByIdAsync(exprectUser.Id.ToString(), CancellationToken.None)).ReturnsAsync(exprectUser);
var userManager = new UserManager<ApplicationUser>(mockUserStore.Object, null, null, null, null, null, null, null, null);
however when the method i am testing tries to find the user.
var findUser = await userManager.FindByEmailAsync("test#test.com");
it throws an error
Message: System.NotSupportedException : Store does not implement IUserEmailStore.
How do i implement IUserEmailStore in my moq usermanager?
My unit test project does contain the newest EntityFramework package.
Trying another way.
var founduser = userManager.Users.FirstOrDefault(e => e.Email.Equals("test#test.com", StringComparison.InvariantCultureIgnoreCase));
results in
System.NotSupportedException : Store does not implement IQueryableUserStore.
I think i must be moqing this wrong.
Update From comment
Ok i can moq the IUserEmailStore but I am not sure what i should do with it
var mockEmailStore = new Mock<IUserEmailStore<ApplicationUser>>();
I managed to create a full moq usermanager that lets me search on email
public class MoqUserManager : UserManager<ApplicationUser>
{
public MoqUserManager(IUserStore<ApplicationUser> userStore) : base(userStore,
new Mock<IOptions<IdentityOptions>>().Object,
new Mock<IPasswordHasher<ApplicationUser>>().Object,
new IUserValidator<ApplicationUser>[0],
new IPasswordValidator<ApplicationUser>[0],
new Mock<ILookupNormalizer>().Object,
new Mock<IdentityErrorDescriber>().Object,
new Mock<IServiceProvider>().Object,
new Mock<ILogger<UserManager<ApplicationUser>>>().Object)
{ }
public override Task<ApplicationUser> FindByEmailAsync(string email)
{
return Task.FromResult(new ApplicationUser { Email = email });
}
}
which gives me
var mockUserStore = new Mock<IUserStore<ApplicationUser>>();
mockUserStore.Setup(x => x.FindByIdAsync(exprectUser.Id.ToString(), CancellationToken.None)).ReturnsAsync(exprectUser);
var userManager = new FakeUserManager(mockUserStore.Object);
So now i can verify that the proper user is returned from my identity server matching the federated login user.
Okay your with the updated question the issue lies in
var userManager = new UserManager<ApplicationUser>(mockUserStore.Object, null, null, null, null, null, null, null, null);
This is not creating a mock, but an actual instance of UserManager<T>.
You will have to do
var userManagerMock = new Mock<UserManager<ApplicationUser>>(mockUserStore.Object, null, null, null, null, null, null, null, null);
then do an setup
userManagerMock.Setup(um => um.FindByEmailAsync("test#email.com)).Returns(exprectUser)
and pass userManagerMock.Object to your
var (user, provider, providerUserUserName, claims, errorMessage) = await AuthorizationHelpers.FindUserFromExternalProvider(authenticateResult, userManagerMock.Object, logger);
When mocking, you never want to call new on the external dependency and instead mock it, since then you can't change its behavior for a specific test. UserManager<T> should have all or most public properties as virtual, so you can override them.
I'm trying to initialize some test data in my database, and having some problems adding password to the ApplicationUser in Identity Framework.
When I have done this earlier, I have used the seed method like this:
protected override void Seed(ApplicationDbContext db)
{
var userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(db));
var adminuser = new ApplicationUser { Email = "admin#test.no", UserName = "admin#test.no", FirstName = "admin", LastName = "test" };
userManager.Create(adminuser, "Password1.");
userManager.AddToRole(adminuser.Id, role:"admin");
}
but as the seed method is not supported in dotnet core I have tried to add the dummy data in the following way:
using (var serviceScope =
app.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope())
db.Database.EnsureCreated();
db.AddRange(
new ApplicationUser { UserName = "ola#nordmann.no", Email = "ola#nordmann.no" },
new ApplicationUser { UserName = "test#test.no", Email = "test#test.no" }
);
db.SaveChanges();
I have also tried to use the same method I used in the seed method, but that doesn't work either as I get the following error message on the line were I add result1 to the database:
cannot convert from 'System.Threading.Tasks.Task' to 'Server.Models.ApplicationUser
using (var serviceScope =
app.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope())
{
var db = serviceScope.ServiceProvider.GetService<ApplicationDbContext>();
var userManager = app.ApplicationServices.GetService<UserManager<ApplicationUser>>();
var au1 = new ApplicationUser { UserName = "test#test.no", Email = "test#test.no" };
var result1 = userManager.CreateAsync(au1, "Test123");
db.Users.Add(result1);
You can use PasswordHasher in combination with user.
i.e.
var user = new User();
var hasher = new PasswordHasher<User>();
db.AddRange(
new ApplicationUser { UserName = "ola#nordmann.no", Email = "ola#nordmann.no", PasswordHash = hasher.HashPassword(user, "Password1") },
);
db.SaveChanges();
If anyone else should be interested, I ended up with the following solution:
public async Task CreateUsersAndRoles(IServiceScope serviceScope)
{
var userManager = serviceScope.ServiceProvider.GetService<UserManager<ApplicationUser>>();
await userManager.CreateAsync(new ApplicationUser { UserName = "ola#nordmann.no", Email = "ola#nordmann.no"}, "Password1.");
}
and call on this method within the Configure method in the startup file like this:
await CreateUsersAndRoles(app.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope());
I started a .net web forms project, the template already allows users to login and register, On the manage page there is an option to reset password. how can I add options to update FirstName and LastName?
This is the code to create a new user:
var manager = Context.GetOwinContext().GetUserManager<ApplicationUserManager>();
var signInManager = Context.GetOwinContext().Get<ApplicationSignInManager>();
var user = new ApplicationUser() { UserName = Email.Text, Email = Email.Text, FirstName = FirstName.Text, LastName = LastName.Text, PhoneNumber = PhoneNumber.Text };
IdentityResult result = manager.Create(user, Password.Text);
if (result.Succeeded)
{
signInManager.SignIn( user, isPersistent: false, rememberBrowser: false); //not needed
IdentityHelper.RedirectToReturnUrl(Request.QueryString["ReturnUrl"], Response); //not needed
}
else
{
ErrorMessage.Text = result.Errors.FirstOrDefault();
}
this is how the form looks like:
This is how I solved it: hope it helps anybody.
var currentUserId = HttpContext.Current.User.Identity.GetUserId();
var context = new ApplicationDbContext();
var user = context.Users.FirstOrDefault(u => u.Id == currentUserId);
if (user != null)
{
if (FirstName.Text != "") user.FirstName = FirstName.Text;
if (LastName.Text != "") user.LastName = LastName.Text;
if (PhoneNumber.Text != "") user.PhoneNumber = PhoneNumber.Text;
if(Email.Text != "") user.Email = Email.Text;
}
var userStore = new UserStore<ApplicationUser>(context);
var userManager = new UserManager<ApplicationUser>(userStore);
var result = userManager.Update(user);
context.SaveChanges();
I am creating a new User using ASP.NET Core Identity as follows:
new User {
Email = "john#company.com",
Name = "John"
}
await userManager.CreateAsync(user, "password");
I need to add a Claims when creating the user. I tried:
new User {
Email = "john#company.com",
Name = "John",
Claims = new List<Claim> { /* Claims to be added */ }
}
But Claims property is read only.
What is the best way to do this?
You can use UserManager<YourUser>.AddClaimAsync method to add a claims to your user
var user = new User {
Email = "john#company.com",
Name = "John"
}
await userManager.CreateAsync(user, "password");
await userManager.AddClaimAsync(user, new System.Security.Claims.Claim("your-claim", "your-value"));
Or add claims to the user Claims collection
var user = new User {
Email = "john#company.com",
Name = "John"
}
user.Claims.Add(new IdentityUserClaim<string>
{
ClaimType="your-type",
ClaimValue="your-value"
});
await userManager.CreateAsync(user);
I came across a problem for seeding the database with Identity v2. I separated out the IdentityModel from the MVC5 project to my Data Access Layer where I setup EF Migrations as well. So I commented out the code which use inside "IdentityConfig.cs" to create initial user and put the code inside my seed database that looks like this
protected override void Seed(Repository.DataContext.IdentityDb context)
{
// var userManager = HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>();
// var roleManager = HttpContext.Current.GetOwinContext().Get<ApplicationRoleManager>();
var owinContext = new OwinContext();
var userManager = owinContext.GetUserManager<ApplicationUserManager>();
var roleManager = owinContext.Get<ApplicationRoleManager>();
const string name = "admin#admin.com";
const string password = "Admin#123456";
const string roleName = "Admin";
// //Create Role Admin if it does not exist
var role = roleManager.FindByName(roleName);
if (role == null)
{
role = new IdentityRole(roleName);
var roleresult = roleManager.Create(role);
}
var user = userManager.FindByName(name);
if (user == null)
{
user = new ApplicationUser { UserName = name, Email = name };
var result = userManager.Create(user, password);
result = userManager.SetLockoutEnabled(user.Id, false);
}
// // Add user admin to Role Admin if not already added
var rolesForUser = userManager.GetRoles(user.Id);
if (!rolesForUser.Contains(role.Name))
{
var result = userManager.AddToRole(user.Id, role.Name);
}
}
Now when I am running command update-database, I got an error
Value cannot be null.
Parameter name: manager
It looks like, I am getting null in these two lines of code
var userManager = owinContext.GetUserManager<ApplicationUserManager>();
var roleManager = owinContext.Get<ApplicationRoleManager>();
Any suggestion please?
This is the way to avoid using an OWIN context:
protected override void Seed(Repository.DataContext.IdentityDb context)
var roleStore = new RoleStore<IdentityRole>(context);
var roleManager = new RoleManager<IdentityRole>(roleStore);
var userStore = new UserStore<ApplicationUser>(context);
var userManager = new UserManager<ApplicationUser>(userStore);
var user = new ApplicationUser { UserName = "sallen" };
userManager.Create(user, "password");
roleManager.Create(new IdentityRole { Name = "admin" });
userManager.AddToRole(user.Id, "admin");
}
I got this working by using:
protected override void Seed(ApplicationDbContext context)
{
context.Configuration.LazyLoadingEnabled = true;
//var userManager = HttpContext.Current
// .GetOwinContext().GetUserManager<ApplicationUserManager>();
//var roleManager = HttpContext.Current
// .GetOwinContext().Get<ApplicationRoleManager>();
var roleStore = new RoleStore<ApplicationRole, int, ApplicationUserRole>(context);
var roleManager = new RoleManager<ApplicationRole, int>(roleStore);
var userStore = new UserStore<ApplicationUser, ApplicationRole, int, ApplicationUserLogin, ApplicationUserRole, ApplicationUserClaim>(context);
var userManager = new UserManager<ApplicationUser, int>(userStore);
...
Hi Under the Startup class please make sure that you have call
app.CreatePerOwinContext(ApplicationDbContext.Create); app.CreatePerOwinContextApplicationUserManager.Create);
app.CreatePerOwinContextApplicationSignInManager.Create);
app.CreatePerOwinContext(ApplicationRoleManager.Create);
Latest stuff is all async & uses Claims.
Here's what worked for me with migrations to add a super user if none exists ...
protected override void Seed(Holos.Service.Models.ApplicationDbContext context)
{
var email = "xxxx#xxxx.com";
var password = "xxxxx";
var userStore = new UserStore<ApplicationUser>(context);
var userManager = new ApplicationUserManager(userStore);
var user = userManager.FindByEmailAsync(email).Result;
if (user == null)
{
var adminUser = new ApplicationUser() { Email = email, UserName = email };
var result = userManager.CreateAsync(adminUser, password);
result.Wait();
userManager.AddClaimAsync(adminUser.Id, new Claim("Read", "*")).Wait();
userManager.AddClaimAsync(adminUser.Id, new Claim("Create", "*")).Wait();
userManager.AddClaimAsync(adminUser.Id, new Claim("Update", "*")).Wait();
userManager.AddClaimAsync(adminUser.Id, new Claim("Delete", "*")).Wait();
userManager.AddClaimAsync(adminUser.Id, new Claim("UserType", "SuperUser")).Wait();
}
}