Search code examples
c#identityserver4asp.net-core-identity

Asp.Net core identity - UserManager.Users.Any() returns always false


This is my first time trying to create an IDP. I chose to do it with Asp.Net Core Identity (3.1.4) and IdentityServer4 (3.1.3). Everything worked fine at first when I was just using the IdentityUser, but yesterday I wanted to add some properties to my user, so I decided to add a class ApplicationUser to hold these properties, ApplicationUser derives of course from IdentityUser.

So, I replaced all the instances where I was using IdentityUser for ApplicationUser and thought that was the end of it. But lo and behold today when I tried to compile to test my work, I found that the seeding method to add users to the database after creation is not finding any users and therefore always trying to add the same users over and over again.

I found that this bit of code is not working as it should. Since now, it always gives me false, despite having 3 users created in the db already. Although I must say the users were created before I added ApplicationUser to the project.

var userManager = scope.ServiceProvider.GetRequiredService<UserManager<ApplicationUser>>();
if (!userManager.Users.Any())
{
    string password = string.Empty;
    foreach (var testUser in Users.Get())
    {
        var identityUser = new ApplicationUser(testUser.Username)
        {
            Id = testUser.SubjectId,
        };
        password = testUser.Password;
        var passwordValidators = userManager.PasswordValidators;
        List<string> passwordErrors = new List<string>();
        foreach (var validator in passwordValidators)
        {
            IdentityResult result = await validator.ValidateAsync(userManager, null, password);

            if (!result.Succeeded)
            {
                foreach (var error in result.Errors)
                {
                    passwordErrors.Add(error.Description);
                }
            }
        }
        if (string.IsNullOrEmpty(password) || passwordErrors.Count > 0)
        {
            password = "Password123!";
        }

        userManager.CreateAsync(identityUser, password).Wait();
        userManager.AddClaimsAsync(identityUser, testUser.Claims.ToList()).Wait();
    }
}

If you'd like to see what is in my startup.cs in ConfigureServices for the IdentityServer and net core Identity, here it is:

string connectionStringISD = Configuration.GetConnectionString("IdentityServerData");
string connectionStringUDB = Configuration.GetConnectionString("UsersDB");

string migrationsAssembly = typeof(Startup)
.GetTypeInfo().Assembly.GetName().Name;

services.AddDbContext<IDPContext>(o => o.UseSqlServer(connectionStringUDB, so => so.MigrationsAssembly(migrationsAssembly)));

services.AddIdentity<ApplicationUser, IdentityRole>()//options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<IDPContext>()
.AddDefaultTokenProviders();

var IS4Builder = services.AddIdentityServer(options =>
{
    options.Events.RaiseErrorEvents = true;
    options.Events.RaiseInformationEvents = true;
    options.Events.RaiseFailureEvents = true;
    options.Events.RaiseSuccessEvents = true;
    options.UserInteraction.LoginUrl = "/Account/Login"; //do i need this?
    options.UserInteraction.LogoutUrl = "/Account/Logout"; //do I need this?
    options.Authentication = new AuthenticationOptions()
    {
        CookieSlidingExpiration = true
    };
})
// this adds the operational data from DB (codes, tokens, consents)
.AddOperationalStore(options => {
    options.ConfigureDbContext = builder =>
    builder.UseSqlServer(connectionStringISD, sqlOptions => sqlOptions.MigrationsAssembly(migrationsAssembly));
    options.EnableTokenCleanup = true;
})
// this adds the config data from DB (clients, resources)
.AddConfigurationStore(options =>
options.ConfigureDbContext = builder =>
builder.UseSqlServer(connectionStringISD, sqlOptions => sqlOptions.MigrationsAssembly(migrationsAssembly)))
.AddAspNetIdentity<ApplicationUser>();

if (Environment.IsDevelopment())
{
    IS4Builder.AddDeveloperSigningCredential();
}
else
{
    IS4Builder.AddSigningCredential(LoadCertificateFromStore(Configuration.GetValue<string>("SigningCertificate")));
}

Also here is how I call the seeding method in the Configure method

 ConfigurationDbContextExtension.InitializeDbDataAsync(app).Wait();

Any ideas? I'd like to avoid having to destroy and recreate the database if possible.


Solution

  • So, after a bit more digging, I found out that because I already had my AspNetUsers table created from when I initially used IdentityUser, I needed to drop the existing local database to avoid possible table collisions. Unfortunately adding and successfully applying the migration was not the answer