Back to all posts

Seeding Identity Users with ASP.NET Core

Posted on Sep 06, 2024

Posted in category:
Development
ASP.NET

ASP.NET Identity is easy to implement and provides many options for building applications.  However, simple tasks such as ensuring that your application has all the needed roles are not as straightforward as one might expect them to be.  In this post I will explore a few options that you might use within your application to perform the setup.

Seeding Values

This seems to be a popular option for users, to utilise the built-in Entity Framework methods for .HasData() and manually insert/create the entries.  I personally find this a bit cumbersome and prone to breaking as you need to ensure that you have all of you data inserted the way that Identity would, and changes in the future could break things.

Utilize an Initialization Code

My preferred method is to introduce an initialization step after your application has been built, but before it has been run.  This allows you to hook into the service provide and create a scope to utilize dependency injection as needed, the impact to application startup time is minimal, and it could be conditionally turned away with a build flag, configuration setting, or otherwise.  To implement this I typically will add 2 methods and one block of code to do the initialization.

The first method will add any needed roles to the system.

Sample Role Seeder
private static async Task EnsureRoles(IServiceProvider serviceProvider)
{
    var roleManager = serviceProvider.GetRequiredService<RoleManager>();

    var existingRole = await roleManager.RoleExistsAsync("Admin");
    if (!existingRole)
    {
        await roleManager.CreateAsync(new Role { Name = "Admin" });
    }
    // This can be repeated as needed
}    

The second method will add any needed users with default credentials that you can change later.

Sample User Setup
private static async Task EnsureUsers(IServiceProvider serviceProvider)
{
    var userManager = serviceProvider.GetRequiredService<UserManager>();
    var existingUser = await userManager.FindByEmailAsync("MyEmail");
    if (existingUser == null)
    {
        var newUser = new UserProfile()
        {
            Email = "MyEmail",
            UserName = "MyEmail",
            EmailConfirmed = true,
            FirstName = "Mitchel",
            LastName = "Sellers",
            PhoneNumber = "MyNumber"
        };
        await userManager.CreateAsync(newUser, "MySPecialPassword");
        await userManager.AddToRolesAsync(newUser, new[] { "Admin" });
    }
} 

The final portion is to wire this up after your call to "builder.Build()" within your Program.cs.

Actual Initialization Code
using (var scope = app.Services.CreateScope())
{
    var serviceProvider = scope.ServiceProvider;
    await EnsureRoles(serviceProvider);
    await EnsureUsers(serviceProvider);
}

The above snippet creates an application scope, and then passed the ServiceProvider into the helper method allowing it to retrieve DI created items and then perform the actions, just like you would if created via normal methods.

In Conclusion

This is a quick fix, but it has worked well!  If you have come up with another solution for this common problem, feel free to share it below!