Custom User Management in ASP.NET Core MVC with Identity
In this article, let’s go in-depth and understand the functionalities you can achieve with the help of Microsoft Identity. We will build a small yet practical implementation of Custom User Management in ASP.NET Core MVC with Identity. This will cover most of the practical use cases involved while developing User Management in ASP.NET Core. You can find the source code of this implementation on my GitHub.
PRO TIP : This is yet another huge article with around 4600+ words (Probably my longest article till now). Do bookmark this page and continue ?
What we’ll learn?
Here are the major topics we will cover in the article.
- Basics of Identity
- Adding Custom Identity Properties
- Seeding Default Users and Roles
- Role Management
- Assigning Users to Roles
- and much more.
What we’ll build?
Here is a small demo of the User Management Module that we are going to build.

Microsoft Identity – Overview
Every time you build an application, the first point of concern is “How will User Management work”, right? Even though this can get complex at times, the basic essence of the requirement is always the same, which is to register, login, authorize users, roles, and so on. So, to help ease the user management process, Microsoft comes up with a default implementation of User Management. The name is Microsoft Identity, It also has built-in UI to support various user functionalities. So developers who are looking for a faster way to implement User Management, tend to go with Identity. You can learn more about Identity here.
Now, Identity comes with certain basic features out of the box. But in real-time scenarios, we may need much more than what Microsoft offers by default. This includes adding Profile Pictures, UI for Role Management, Custom logic to log in to the user, and much more. This is exactly what we will learn in the course of this article.
Setting up the ASP.NET Core MVC Application
We will start off by creating a new ASP.NET Core 3.1 MVC Project with Authentication Mode selected as Individual User Accounts. This activates Microsoft Identity by default.

When I got started with Microsoft Identity for the first time in an ASP.NET Core 3.1 Application, the biggest confusion that hit me was, “Where are the Views/Controllers for the Identity? How does the Login page get rendered? Because there is no sign of a login page’s HTML anywhere.” I am sure that most of you have had a similar question somewhere down the line.
So, where are the view and controller located? During the announcement of .NET Core 2, Microsoft made it official that the new project scaffoldings will no longer have the auto-generated code for Identity. Rather, they decided to put all this code and HTML into the Identity DLL. While this is a clean way of going about with identity, it makes it confusing for developers who want to add custom features on top of the Identity.
Scaffolding the Identity UI
In order to modify the existing Identity, Visual Studio allows you to generate the Identity Pages. You can achieve this by right-clicking the project and Adding a new Scaffolded Item. On the dialog that appears, select the Identity and click Add. After that, you will be presented with a form containing over 50 options to select from! These are the Razor Page Versions of the Identity UI. Here you can choose the files you want to add to your project. For now, let’s select everything. Also, select the default Data context class that was already generated for you while creating the project. Click Add.

Once it is added, you can see a number of razor pages in the Areas folder. These are files that act as the default Identity UI. Moving further we will learn about customizing Identity to match our requirements.

Renaming the Default Identity Tables and Updating.
Before moving on, let’s update the database. As soon as we created our project, Visual Studio has done the following for us already.
- Added migrations for the Identity Table.
- Generated a default DB Context
- Registered the DB Context in the Startup.cs
- Added a default connection string to appsettings.json (a local DB with the project name and GUID)
PS – You can change the connection string to that of the DBMS of your choice. To keep the article simple, I am going on with the local db connection.
Since everything is set up for us, let’s apply the migrations and update the database. Open up the package manager console and type in the following.
update-database
Once that’s done, open up the SQL Server Object Explorer in Visual Studio. You can see our newly generated Identity tables here.

Now, there is one thing that catches the eyes of many. The Table Names. Quite ugly with the ASPNET Naming convention, right? Let’s change that now.
We will delete our new database for now.
drop-database
Also, delete the migrations folder (found inside the Data Folder), as we are going to generate a new one. Here is a simple solution. Since we are by default using Entity Framework Core, let’s open up the ApplicationDbContext.cs from the Data Folder. To modify the default ugly names of the Identity Tables, add this override function,
protected override void OnModelCreating(ModelBuilder builder) { base.OnModelCreating(builder); builder.HasDefaultSchema("Identity"); builder.Entity<IdentityUser>(entity => { entity.ToTable(name: "User"); }); builder.Entity<IdentityRole>(entity => { entity.ToTable(name: "Role"); }); builder.Entity<IdentityUserRole<string>>(entity => { entity.ToTable("UserRoles"); }); builder.Entity<IdentityUserClaim<string>>(entity => { entity.ToTable("UserClaims"); }); builder.Entity<IdentityUserLogin<string>>(entity => { entity.ToTable("UserLogins"); }); builder.Entity<IdentityRoleClaim<string>>(entity => { entity.ToTable("RoleClaims"); }); builder.Entity<IdentityUserToken<string>>(entity => { entity.ToTable("UserTokens"); }); }
Line #4, sets a schema for the database.
Line #7, renames the User Table from ASPNETUsers to Identity.User. Clean enough? Feel free to add tables names that can make more sense to you, Similarly we rename all the table entries.
With that out of the way, let’s add the migrations and update the database.
add-migration "Renamed Identity Table Names" update-database
Let’s see the DB now.

This looks better, doesn’t it?
Adding Custom Fields to Identity User
If you go through the Identity.User Table, you can find over 10-15 columns that are available by default. What if we wanted to add certain user-specific properties like First Name, Image, and so on?
This is where you will need to extend the IdentityUser class with your own properties. In the Models folder, create a new class Models/ApplicationUser.cs and inherit the Identity User.
public class ApplicationUser : IdentityUser { public string FirstName { get; set; } public string LastName { get; set; } public int UsernameChangeLimit { get; set; } = 10; public byte[] ProfilePicture { get; set; } }
Here, we are adding properties like FirstName, LastName, a byte array of Image, and UserNameChangeLimit (we will talk about this particular property later on.)
Since we decided to change the default User class from IdentityUser to ApplicationUser, we would have to make other changes in our existing code as well. Firstly, we will need to change how the User class is being wired up with the DbContext. Navigate to Startup.cs/ConfigureServices method and modify it.
public void ConfigureServices(IServiceCollection services) { services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); services.AddIdentity<ApplicationUser, IdentityRole>() .AddEntityFrameworkStores<ApplicationDbContext>() .AddDefaultUI() .AddDefaultTokenProviders(); services.AddControllersWithViews(); services.AddRazorPages(); }
After that comes the hard and time-consuming part. Remember the 40+ Identity pages we imported via scaffolding? Now, each of these 40 pages would have a reference to IdentityUser class. We will have to replace each of them with ApplicationUser. A simple step to get started is to Search for IdentityUser in the entire solution with Visual Studio and to replace it with Application User. But even after that, you would have to manually go to each page to add the namespace reference. Once you have resolved each of the namespace issues, add the final change. Navigate to ApplicaionDbContext and modify the first line of the class to accommodate ApplicationUser.
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
Next, build the application to check for build errors. If you find any, resolve it by adding the required namespace.
Finally, let’s run the commands and update the database.
add-migration "Added Custom Properties" update-database

As you can see, we have added additional fields to the Identity Table.
Extending the Registration Form
Now that we have added the extra fields, let’s use them in the registration process. Navigate to Areas/Identity/Pages/Account/Register.cshtml. This is a Razor Page which also holds the Register.cs file within itself.

If you are new to Razor pages, the cshtml holds all the Razor engine contents (C# + HTML) and the cs file contains Controller-like C# code.
Let’s run the application and navigate to the Register Page.

In practical cases, one would have to enter his name while registering. Let’s extend this form and add the First Name and Last name fields. I will split this task into two parts
- Modifying the HTML content to add 2 new fields in the form
- Wiring these forms up with C# code to save them to the database while registering
Let’s first get done with the C# part. Navigate to Register.cs. In the InputModel class, let’s add 2 new properties, First Name, and Last Name.
public class InputModel { [Required] [Display(Name = "First Name")] public string FirstName { get; set; } [Required] [Display(Name = "Last Name")] public string LastName { get; set; } . . . }
PS – I have added dots (.) to denote that we keep the exising code. This is just to keep the code snippets smaller and to the point.
Next, we will need to pass data to these properties and save it to the DB while registering. For this, open up the OnPostAsync method in the Register.cs class.
if (ModelState.IsValid) { MailAddress address = new MailAddress(Input.Email); string userName = address.User; var user = new ApplicationUser { UserName = userName, Email = Input.Email, FirstName = Input.FirstName, LastName = Input.LastName }; . . }
Here we are doing 2 things
- Line 5 – 10 Adding new fields to the registration model and linking with C#
- Line 3 – 4 Generating a username from the email address. For example, if the email address entered by the user is admin@demo.com, the username generated will be admin. You can implement your own logic here.
This is all you have to do with the C# code. Let’s add the fields to the Register.cshtml page.
<div class="form-group"> <label asp-for="Input.FirstName"></label> <input asp-for="Input.FirstName" class="form-control" /> <span asp-validation-for="Input.FirstName" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="Input.LastName"></label> <input asp-for="Input.LastName" class="form-control" /> <span asp-validation-for="Input.LastName" class="text-danger"></span> </div>
Let’s check out the result. Build and run the application. Navigate to the Register Page. You can see the new fields now. Add some details to the form and try to register.

You can see that the application redirects you to the home page. In the navigation menu, you can see the generated username too.

Now try to logout and log in back to the application. You will not be able to. Any Guesses?
By default, in Identity, the username and Email are the same. Now, in the login form, the application expects the username in the email field. But our username is no longer an email id, remember? Let’s fix this next.
Allow Login with both Username and Email
Ideally, you may want to allow your users to log in with both the username and the email id. This is standard practice. Let’s get started. Navigate to Areas/Identity/Pages/Account/Login.cs
public class InputModel { [Required] [Display(Name = "Email / Username")] public string Email { get; set; } [Required] [DataType(DataType.Password)] public string Password { get; set; } [Display(Name = "Remember me?")] public bool RememberMe { get; set; } }
At the model level, we made the Email Property accept both email ids and plain text.
In the Login.cs , add a new function to check if the entered data is a valid email id or not.
public bool IsValidEmail(string emailaddress) { try { MailAddress m = new MailAddress(emailaddress); return true; } catch (FormatException) { return false; } }
public async Task<IActionResult> OnPostAsync(string returnUrl = null) { returnUrl = returnUrl ?? Url.Content("~/"); if (ModelState.IsValid) { var userName = Input.Email; if (IsValidEmail(Input.Email)) { var user = await _userManager.FindByEmailAsync(Input.Email); if (user != null) { userName = user.UserName; } } var result = await _signInManager.PasswordSignInAsync(userName, Input.Password, Input.RememberMe, lockoutOnFailure: false); if (result.Succeeded) { _logger.LogInformation("User logged in."); return LocalRedirect(returnUrl); } . . } return Page(); }
Line 8 – Checks if the entered data is a valid Email of Not.
Line 9 – 14 – If it’s a valid email, we would want to get the username associated with the particular email id.
Line 10 – Get the User for the EmailId.
Line 13 – From the user object, get the username.
Line 16 – Passes the UserName to the SigninManager.
Build the application and run. You would be able to log in with both the username and the email id now.
PS, Since we are making quite a lot of changes, it is advisable to clean the solution once in a while to rebuild the entire project from scratch. There are chances that Visual Studio would not re-build few of the pages.
Log in to your application and click on the “Hello {username}” in the top navigation menu. You will be redirected to the “Manage your account” Page. There is quite a lot of basic options here, like changing your phone number, updating the email id, changing the password, and so on. Let’s try to extend these pages in the coming sections.
Adding the Custom User Fields To Profile Settings
Here is my default “Manage your account”. Here, we would like to add the custom fields that we made earlier. First Name, Last Name, Photo, remember?

As the first step, let’s try to add the First name and Last name fields to this form. Navigate to Areas/Identity/Pages/Account/Manage/Index.cshtml.cs
Replace the Input Model. Here we added the new fields. (including Profile Picture, although we will implement it in the next section)
public class InputModel { [Display(Name = "First Name")] public string FirstName { get; set; } [Display(Name = "Last Name")] public string LastName { get; set; } [Display(Name = "Username")] public string Username { get; set; } [Phone] [Display(Name = "Phone number")] public string PhoneNumber { get; set; } [Display(Name = "Profile Picture")] public byte[] ProfilePicture { get; set; } }
Next, while loading the form we need to load these data to the memory as well.
private async Task LoadAsync(ApplicationUser user) { var userName = await _userManager.GetUserNameAsync(user); var phoneNumber = await _userManager.GetPhoneNumberAsync(user); var firstName = user.FirstName; var lastName = user.LastName; var profilePicture = user.ProfilePicture; Username = userName; Input = new InputModel { PhoneNumber = phoneNumber, Username = userName, FirstName = firstName, LastName = lastName, ProfilePicture = profilePicture }; }
Finally, in the OnPostAsync method, add this to update the newly entered FirstName or LastName.
var firstName = user.FirstName; var lastName = user.LastName; if (Input.FirstName != firstName) { user.FirstName = Input.FirstName; await _userManager.UpdateAsync(user); } if (Input.LastName != lastName) { user.LastName = Input.LastName; await _userManager.UpdateAsync(user); }
Let’s now add the HTML definition of the form fields. Navigate to the Index.cshtml
<div class="form-group"> <label asp-for="Input.FirstName"></label> <input asp-for="Input.FirstName" class="form-control" /> <span asp-validation-for="Input.FirstName" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="Input.LastName"></label> <input asp-for="Input.LastName" class="form-control" /> <span asp-validation-for="Input.LastName" class="text-danger"></span> </div>
That’s it. Build and Run your application. Go to the “Manage your account” page. You will see the changes here.

Adding a Profile Picture
As the next step, let’s add a profile picture to our User. Since we have already added the ProfilePicture property in our Input model, let’s configure the saving part.
Remember the part where we added the Update code for FirstName and Lastname? Similarly, in the OnPostAsync method, let’s save the Profile Picture as well.
if (Request.Form.Files.Count > 0) { IFormFile file = Request.Form.Files.FirstOrDefault(); using (var dataStream = new MemoryStream()) { await file.CopyToAsync(dataStream); user.ProfilePicture = dataStream.ToArray(); } await _userManager.UpdateAsync(user); }
In this case, we are trying to save our image to the database. Thus we use memory streams to convert the image file to a memory object/byte array. Then we save this array to the Database. If you want to learn more about uploading images/files/multiple-files to the database/file system, I have written a detailed article where I build a small application that can upload files to the database as well as to the file system. Check it out here.
Finally, let’s modify the Index.cshtml to add the container for images. You can have your approach to do this. End of the day, it will be just an Image Input that is bound to the ProfilePicture Property. I have added several customizations to it, which you might be interested in as well.
<form id="profile-form" method="post" enctype="multipart/form-data"> <div class="row"> <div class="col-md-6"> <div asp-validation-summary="ModelOnly" class="text-danger"></div> <div class="form-group"> <label asp-for="Input.FirstName"></label> <input asp-for="Input.FirstName" class="form-control" /> <span asp-validation-for="Input.FirstName" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="Input.LastName"></label> <input asp-for="Input.LastName" class="form-control" /> <span asp-validation-for="Input.LastName" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="Input.Username"></label> <input asp-for="Input.Username" class="form-control" /> <span asp-validation-for="Input.Username" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="Input.PhoneNumber"></label> <input asp-for="Input.PhoneNumber" class="form-control" /> <span asp-validation-for="Input.PhoneNumber" class="text-danger"></span> </div> <button id="update-profile-button" type="submit" class="btn btn-primary">Save</button> </div> <div class="col-md-6"> <div asp-validation-summary="ModelOnly" class="text-danger"></div> <div class="form-group"> <label asp-for="Input.ProfilePicture" style="width: 100%;"></label> @if (Model.Input.ProfilePicture != null) { <img id="profilePicture" style="width:350px;height:350px; object-fit:cover" src="data:image/*;base64,@(Convert.ToBase64String(Model.Input.ProfilePicture))"> } else { <img id="profilePicture" style="width:350px;height:350px; object-fit:cover" src=""> } <input type="file" accept=".png,.jpg,.jpeg,.gif,.tif" asp-for="Input.ProfilePicture" class="form-control" style="border:0px!important;padding: 0px;padding-top: 10px;padding-bottom: 30px;" onchange="document.getElementById('profilePicture').src = window.URL.createObjectURL(this.files[0])" /> <span asp-validation-for="Input.ProfilePicture" class="text-danger"></span> </div> </div> </div> </form>
Line #1- Here, we change the form type to “multipart/form-data”. This is because we not only have plain texts but Image files too.
Line 29-46 – Here is where we define our Image Field.
Line 31-38 – If the user has an image uploaded to the database already, we get the byte array from the model and convert it to an image. This image will be displayed on the page. Else, shows a blank image container.
Line 39 – Defining the Input Button that can load images from our file system.


You can see that we are now able to upload images to our profile. Here is a little bonus. We have the image only on the Profile Page. Ideally, websites have the profile pictures somewhere in the navigation menu too, right? Let’s do the same.
Navigate to Views/Shared/_LoginPartial.cshtml and add the highlighted code.
@if (SignInManager.IsSignedIn(User)) { <li class="nav-item" style="align-self: center;"> @if (UserManager.GetUserAsync(User).Result.ProfilePicture != null) { <img style="width:40px;height:40px; object-fit:cover; border-radius:30px" src="data:image/*;base64,@(Convert.ToBase64String(UserManager.GetUserAsync(User).Result.ProfilePicture))"> } </li> . . }

Feel free to change the location of this image.
Setting a Limit to Change Username
This is yet another feature that is not covered in any of the articles about Custom User Management in ASP.NET Core. Usernames are quite vital for any User-based application. Let’s take Facebook for example.
https://www.facebook.com/codewithmukesh
Here, ‘codewithmukesh‘ is the username of our Facebook Page. What if we want to change this username to something else. Facebook allows us to do, but only for a specific number of times. Let’s implement this exact feature in our Solution as well.
First, we need a container in the HTML page that would hold a message like “You can change your username x more time(s)”. In order to do this, we use TempData, as ‘x’ is a number that gets generated via our C# end. Let’s create this TempData First. Navigate to Index.cshtml.cs, and add a new TempData Property.
[TempData] public string StatusMessage { get; set; } [TempData] public string UserNameChangeLimitMessage { get; set; } [BindProperty] public InputModel Input { get; set; }
Now, the logic to calculate the number of attempts left for a user to change his/her username. Like we added the logic to save/update FirstName, LastName and so, we will add the logic to save/update the remaining attempts left for the user.
if (user.UsernameChangeLimit > 0) { if (Input.Username != user.UserName) { var userNameExists = await _userManager.FindByNameAsync(Input.Username); if (userNameExists != null) { StatusMessage = "User name already taken. Select a different username."; return RedirectToPage(); } var setUserName = await _userManager.SetUserNameAsync(user, Input.Username); if (!setUserName.Succeeded) { StatusMessage = "Unexpected error when trying to set user name."; return RedirectToPage(); } else { user.UsernameChangeLimit -= 1; await _userManager.UpdateAsync(user); } } }
Line 5 – Check if the username already exists.
Line 6-10 – If the username is already taken, shows an appropriate message.
Line 11 – Else, saves the username to the user object.
Line 19 – Reduces the attempts of the user by 1.
Line 20 – Updates the user.
Modify the OnGetSync Method to load the message by default.
public async Task<IActionResult> OnGetAsync() { var user = await _userManager.GetUserAsync(User); if (user == null) { return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); } UserNameChangeLimitMessage = $"You can change your username {user.UsernameChangeLimit} more time(s)."; await LoadAsync(user); return Page(); }
Now, let’s link the Tempdata to the HTML. Navigate to Index.cshtml
<h4>@ViewData["Title"]</h4> <partial name="_StatusMessage" model="Model.StatusMessage" /> <partial name="_StatusMessage" model="Model.UserNameChangeLimitMessage" />
Build and Run your application. You will be able to change the username 10 times. After which you will be blocked from doing so. Pretty Nice Feature to have, yeah?

User Roles – Overview
Now that we have added custom fields and Images to the User, let’s move on and talk about User Roles. While Building Custom User Management in ASP.NET Core MVC with Identity, Roles are quite important. For example, If we take the case of Invoice Management Application, it would have User Roles like Manager, Operator, Super-Admin, and so on. User Roles help to define the level of permission for each user.
In the upcoming sections, we will talk about Adding User Roles by default to an Application, Assigning the user Roles to Users, and much more.
Seed Default Roles
By default, Visual Studio does not create any Roles for you in the Identity Database. In this section, our task is to statically define the possible Roles supported in an Application and insert them into the database on page load. We will be doing this with the help of the SEEDING concept in Entity Framework Core.
Here is the table where the Roles will be stored.

Let’s assume that this application supports 4 User Roles by default.
1.SuperAdmin
2. Admin
3. Moderator
4. Basic
Create an Enum for the supported Roles. Add a new Enum at Enums/Roles.
public enum Roles { SuperAdmin, Admin, Moderator, Basic }
In the data folder, add a ContextSeed.cs class. This class will be responsible for seeding default data to the database. In this case, let’s add a function to seed the User Roles to the database.
public static class ContextSeed { public static async Task SeedRolesAsync(UserManager<ApplicationUser> userManager, RoleManager<IdentityRole> roleManager) { //Seed Roles await roleManager.CreateAsync(new IdentityRole(Enums.Roles.SuperAdmin.ToString())); await roleManager.CreateAsync(new IdentityRole(Enums.Roles.Admin.ToString())); await roleManager.CreateAsync(new IdentityRole(Enums.Roles.Moderator.ToString())); await roleManager.CreateAsync(new IdentityRole(Enums.Roles.Basic.ToString())); } }
Now we need an entry point to invoke the ContextSeed Methods. It depends on your preference, but for this article, we will invoke the method in the Main Function(Program.cs) . That is, every time the application fires up, It would check if the default user roles are present in the database. Else, it seeds the required Roles.
public async static Task Main(string[] args) { var host = CreateHostBuilder(args).Build(); using (var scope = host.Services.CreateScope()) { var services = scope.ServiceProvider; var loggerFactory = services.GetRequiredService<ILoggerFactory>(); try { var context = services.GetRequiredService<ApplicationDbContext>(); var userManager = services.GetRequiredService<UserManager<ApplicationUser>>(); var roleManager = services.GetRequiredService<RoleManager<IdentityRole>>(); await ContextSeed.SeedRolesAsync(userManager, roleManager); } catch (Exception ex) { var logger = loggerFactory.CreateLogger<Program>(); logger.LogError(ex, "An error occurred seeding the DB."); } } host.Run(); }
That’s it. Just add the required References and Build & Run the Application. Now let’s check our Identity. Role Table and ensure if the default Roles were added.

You can see that all our 4 default roles are already added. Simple, yeah? Similarly in the next section, let’s learn to add a default user (super admin) via seeding.
Seed Default Super Admin user
Let me draw out the requirement. We need to seed a default Super-Admin with pre-defined Names and Add this User to all the Roles Available. How would you go about this?
Step 1 – Add a new Method that seeds the default User.
In our ContextSeed class, let’s add a new method.
public static async Task SeedSuperAdminAsync(UserManager<ApplicationUser> userManager, RoleManager<IdentityRole> roleManager) { //Seed Default User var defaultUser = new ApplicationUser { UserName = "superadmin", Email = "superadmin@gmail.com", FirstName = "Mukesh", LastName = "Murugan", EmailConfirmed = true, PhoneNumberConfirmed = true }; if (userManager.Users.All(u => u.Id != defaultUser.Id)) { var user = await userManager.FindByEmailAsync(defaultUser.Email); if(user==null) { await userManager.CreateAsync(defaultUser, "123Pa$$word."); await userManager.AddToRoleAsync(defaultUser, Enums.Roles.Basic.ToString()); await userManager.AddToRoleAsync(defaultUser, Enums.Roles.Moderator.ToString()); await userManager.AddToRoleAsync(defaultUser, Enums.Roles.Admin.ToString()); await userManager.AddToRoleAsync(defaultUser, Enums.Roles.SuperAdmin.ToString()); } } }
Step 2 – Wire up this Method so that it gets invoked at Application Start.
Like we did in the previous section, add the below line of code to the Program.cs/Main Method. This will invoke the Method that seeds the default user to the database.
await ContextSeed.SeedSuperAdminAsync(userManager, roleManager);
Build the application and try to log in with the default credentials that we defined.

There you go, we have successfully seeded our default user. It was easy, right?
Add A Default Role to Newly Registered User
Now, we know how to seed default roles and users as well. What happens when a user registers himself with the application? What role would he belong to? As of now, None. Let’s add a feature by which any newly registered user automatically gets assigned to the Basic Role.
This will be quite simple as we have already set up the required pieces of code. Navigate to the Register.cs file and add the highlighted line below.
var result = await _userManager.CreateAsync(user, Input.Password); if (result.Succeeded) { _logger.LogInformation("User created a new account with password."); await _userManager.AddToRoleAsync(user, Enums.Roles.Basic.ToString()); var code = await _userManager.GenerateEmailConfirmationTokenAsync(user); . . }
Add / View Available Roles
Let’s start building a UI with which we can View and Manage Roles. It will be a simple view that Lists out all the roles available and has a feature to add new roles too.
Start by adding a new Empty MVC Controller to the Controllers folder. Name it RoleManagerController.cs
public class RoleManagerController : Controller { private readonly RoleManager<IdentityRole> _roleManager; public RoleManagerController(RoleManager<IdentityRole> roleManager) { _roleManager = roleManager; } public async Task<IActionResult> Index() { var roles = await _roleManager.Roles.ToListAsync(); return View(roles); } [HttpPost] public async Task<IActionResult> AddRole(string roleName) { if (roleName != null) { await _roleManager.CreateAsync(new IdentityRole(roleName.Trim())); } return RedirectToAction("Index"); } }
Right-click on the Index Method and click on Add View. Name it Index.cshtml. This creates a new View for this Controller. Modify the View as the following.
@model List<Microsoft.AspNetCore.Identity.IdentityRole> @{ ViewData["Title"] = "Role Manager"; Layout = "~/Views/Shared/_Layout.cshtml"; } <h1>Role Manager</h1> <form method="post" asp-action="AddRole" asp-controller="RoleManager"> <div class="input-group"> <input name="roleName" class="form-control w-25"> <span class="input-group-btn"> <button class="btn btn-info">Add New Role</button> </span> </div> </form> <table class="table table-striped"> <thead> <tr> <th>Id</th> <th>Role</th> </tr> </thead> <tbody> @foreach (var role in Model) { <tr> <td>@role.Id</td> <td>@role.Name</td> </tr> } </tbody> </table>
That’s it. Build and Run the application. Navigate to localhost:xxx/RoleManager. Here you will be able to see all the visible Roles and Add New Roles as well.

Listing Users with Corresponding Roles
Now, let’s make an interface that lists all the users along with the Roles associated with them. We will first create a View Model that holds the user details and the roles as a string List. Create a new class in the Models Folders. Models/UserRolesViewModel.cs
public class UserRolesViewModel { public string UserId { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string UserName { get; set; } public string Email { get; set; } public IEnumerable<string> Roles { get; set; } }
Next, we will create a controller that throws out a view with a list of user details. Essentially it would get all the users from the database and also the roles per user.
public class UserRolesController : Controller { private readonly UserManager<ApplicationUser> _userManager; private readonly RoleManager<IdentityRole> _roleManager; public UserRolesController(UserManager<ApplicationUser> userManager, RoleManager<IdentityRole> roleManager) { _roleManager = roleManager; _userManager = userManager; } public async Task<IActionResult> Index() { var users = await _userManager.Users.ToListAsync(); var userRolesViewModel = new List<UserRolesViewModel>(); foreach (ApplicationUser user in users) { var thisViewModel = new UserRolesViewModel(); thisViewModel.UserId = user.Id; thisViewModel.Email = user.Email; thisViewModel.FirstName = user.FirstName; thisViewModel.LastName = user.LastName; thisViewModel.Roles = await GetUserRoles(user); userRolesViewModel.Add(thisViewModel); } return View(userRolesViewModel); } private async Task<List<string>> GetUserRoles(ApplicationUser user) { return new List<string>(await _userManager.GetRolesAsync(user)); } }
Finally, add a View to the Index Method and Add the Following.
@using UserManagement.MVC.Models @model List<UserManagement.MVC.Models.UserRolesViewModel> @{ ViewData["Title"] = "Index"; Layout = "~/Views/Shared/_Layout.cshtml"; } <h1>User Roles</h1> <table class="table table-striped"> <thead> <tr> <th>First Name</th> <th>Last Name</th> <th>Email</th> <th>Roles</th> <th>Action</th> </tr> </thead> <tbody> @foreach (var user in Model) { <tr> <td>@user.FirstName</td> <td>@user.LastName</td> <td>@user.Email</td> <td>@string.Join(" , ", user.Roles.ToList())</td> <td> <a class="btn btn-primary" asp-controller="UserRoles" asp-action="Manage" asp-route-userId="@user.UserId">Manage Roles</a> </td> </tr> } </tbody> </table>
Run the Application and navigate to /UserRoles. Here you can see the list of users along with the assigned Roles. For instance, you can see that the Superadmin has all the roles assigned.

Adding Users to Roles
As for the last section, Let’s try to modify the Roles of Users. We will add a new Method in the UserRolesController named Manage. Before that, let’s add a Model Class that holds the ViewModel Data.
public class ManageUserRolesViewModel { public string RoleId { get; set; } public string RoleName { get; set; } public bool Selected { get; set; } }
Once that is done, we go to the UserRolesController and add HTTP GET and HTTP POST Variants of the Manage Method. The Get Method will be responsible to get the roles per user. Post Method will handle the role-assigning part of the user.
public async Task<IActionResult> Manage(string userId) { ViewBag.userId = userId; var user = await _userManager.FindByIdAsync(userId); if (user == null) { ViewBag.ErrorMessage = $"User with Id = {userId} cannot be found"; return View("NotFound"); } ViewBag.UserName = user.UserName; var model = new List<ManageUserRolesViewModel>(); foreach (var role in _roleManager.Roles) { var userRolesViewModel = new ManageUserRolesViewModel { RoleId = role.Id, RoleName = role.Name }; if (await _userManager.IsInRoleAsync(user, role.Name)) { userRolesViewModel.Selected = true; } else { userRolesViewModel.Selected = false; } model.Add(userRolesViewModel); } return View(model); } [HttpPost] public async Task<IActionResult> Manage(List<ManageUserRolesViewModel> model, string userId) { var user = await _userManager.FindByIdAsync(userId); if (user == null) { return View(); } var roles = await _userManager.GetRolesAsync(user); var result = await _userManager.RemoveFromRolesAsync(user, roles); if (!result.Succeeded) { ModelState.AddModelError("", "Cannot remove user existing roles"); return View(model); } result = await _userManager.AddToRolesAsync(user, model.Where(x => x.Selected).Select(y => y.RoleName)); if (!result.Succeeded) { ModelState.AddModelError("", "Cannot add selected roles to user"); return View(model); } return RedirectToAction("Index"); }
Finally, add a new View for the Manage Method and Name it Manage.cshtml.
@model List<ManageUserRolesViewModel> @{ ViewData["Title"] = "Manage"; Layout = "~/Views/Shared/_Layout.cshtml"; } <form method="post"> <div class="card"> <div class="card-header"> <h2>Manage User Roles</h2> Add/Remove Roles for User / @ViewBag.UserName. </div> <div class="card-body"> @for (int i = 0; i < Model.Count; i++) { <div class="form-check m-1"> <input type="hidden" asp-for="@Model[i].RoleId" /> <input type="hidden" asp-for="@Model[i].RoleName" /> <input asp-for="@Model[i].Selected" class="form-check-input" /> <label class="form-check-label" asp-for="@Model[i].Selected"> @Model[i].RoleName </label> </div> } <div asp-validation-summary="All" class="text-danger"></div> </div> <div class="card-footer"> <input type="submit" value="Update" class="btn btn-primary" style="width:auto" /> <a asp-action="EditUser" asp-route-id="@ViewBag.userId" class="btn btn-primary" style="width:auto">Cancel</a> </div> </div> </form>
That’s all. Run the Application and go to /UserRoles. Click on the manage button of any User. You will be presented with a checkbox list of assigned User Roles. From here you can do the required modifications and updates. This Redirects you back to the Index method.



You can see that We have a complete working model of User Role Manager UI for ASP.NET Core MVC. I will wind up this article with this.
Further Improvements
You might have noticed that all the Views are accessible to anyone. You could limit this by adding an Authorize Attribute Decorator over the Controller Methods. To be specific, we don’t want to expose the user Roles Manager to the public, do we? On top of the Manage Method in the UserRolesController, add this.
[Authorize(Roles = "SuperAdmin")]
This ensures that only SuperAdmins can Manage User Roles. This is also called Role-based Authentication.
support me
Thank you for visiting. If you like my content and code, support me by buying a couple of coffees so that I can find enough time to research & write new articles. Cheers!
Summary
In this really long article, we have gone through nearly everything that you would need to know to implement Custom User Management in ASP.NET Core MVC with Identity. Here is the complete source code of the above implementations. Do Leave a star if you find it helpful! Did I miss out on something? Comment below and I will try to integrate that too with this article. Leave your suggestions and feedback in the comment section below. Do not forget to share this article within your developer community. Thanks and Happy Coding! 😀
Hi,
I am facing an error in source code when adding a new role.
[HttpPost]
public async Task AddRole(string roleName)
{
await _roleManager.CreateAsync(new IdentityRole(roleName.Trim()));
return RedirectToAction(“Index”);
}
here I got roleName as a null value.
Hi, Only If the Textbox is empty, It throws a null object error. Else it works fine. Have tested it right now. However, I will add a null check to handle the null issue too. Thanks for letting me know.
Regards
Hi,
thank you for replay. that’s my mistake.
can you please do a sample app in the oracle DB with Identity and sample architecture of repository pattern without entity framework.
Hi Kiran, it seems you have found a solution to work with Oracle DB. Can you please share your knowledge.
issue solved
Hi, can we do this using ODP.NET? We use Oracle DB, and our entire IT Do not want to use EF.
Really good!
Thanks a lot! 🙂
Regards
What if I want a list of users in an specific role as an iQueryable, so I can create a PaginatedList?
Very good tutorial.
Thanks a lot, Mukesh
Thanks a lot for the feedback!
Hi, thank you for this article. I want to ask if you also have a post about multi-factor authentication.
Hi, Currently i dont have an article about it. Will add it to my TODO list.
Regards
Thank you I am looking forward to it.
Thanks Mukesh. This was really helpful
Great job!!
Thanks!
Very good tutorial.
Thanks a lot, Mukesh
But, in UserRolesController, I have had to add .ToList()
foreach (var role in _roleManager.Roles.ToList())
This fixed my problem
Thanks a lot.
This was really helpful.You did a great job.
var users = await _userManager.Users.ToListAsync();
sir plz help me this syntax return null refrence exception
Can you check if _userManager is null? If that’s the case you must have missed out the constructor dependency injection or at the Startup.Or else , there might be no users available. Let me know
Regards
So far really helpful!
I am up to the part of trying to seed default users and I am getting an error with CreateHostBuilder. I’ve directly copy pasted your code and it is saying “the name CreateHostBuilder does not exist in the current context”. I am using .NET Core 3.1 and have copied the tutorial identically to this point.
Basically what I am trying to achieve is exactly what you have done, although I would like to have another section to the user configuration where access is restricted to a store (pretty much identical to roles).
Hi, I am not very sure why your code breaks at CreateHostBuilder. Probably you are missing out some namespace. Could you please check the Github Repo and verify if it works for you. https://github.com/iammukeshm/CustomUserManagement.MVC
Thanks
Thanks Mukesh for your help, I have found where the problem was in my code. And have completed the tutorial.
I am now trying to restrict access to certain views and buttons (rather than just the controller) – is this possible?
Yes, you can use the Policy option to control the views
https://docs.microsoft.com/en-us/aspnet/core/security/authorization/views?view=aspnetcore-5.0
Something below what I did for creating Policy based on the Roles (you can use multiple roles though to define policy):
Startup.cs
ConfigureServices method
add below line
services.AddAuthorizationCore(options =>
{
options.AddPolicy(“SuperAdminOnly”, policy => policy.RequireRole(“SuperAdmin”));
options.AddPolicy(“AdminOnly”, policy => policy.RequireRole(“Admin”));
});
Then in the Views,
added
@using Microsoft.AspNetCore.Authorization
@inject IAuthorizationService AuthorizationService
@if ((await AuthorizationService.AuthorizeAsync(User, “SuperAdmin”)).Succeeded)
{
This text is only visible for user with SuperAdmin policy
}
Thank you Arunkumar for your help! This help a lot!
InvalidOperationException: There is already an open DataReader associated with this Connection which must be closed first.
it error on here.
if( await _userManager.IsInRoleAsync(user, role.Name))
please help brother
Problem is the foreach is looping over the roles like this:
foreach (var role in _roleManager.Roles)
Because this is an IQuerable the DataReaders connection will stay open until each role has been read.
Solution:
foreach (var role in _roleManager.Roles.ToList())
This way all the roles are loaded.
Thank you a million times!
Ctrl + Shift + h
ApplicationUser
Models.ApplicationUser
Entire solution
*.cshtml.cs
This is in order to update all Pages simultaneously
User name already taken. Select a different username. This error never show.
I have followed your guide and can’t find any mistakes, however, I am getting a System.InvalidOperationException: ‘Scheme already exists: Identity.Application, in Program.cs on host.Run().
Comparing my files with that on your Github Repo I am unable to find a mistake… Any suggestions?
https://stackoverflow.com/questions/52522248/store-does-not-implement-iuserrolestoretuser-asp-net-core-identity
same thing and update the connection string to
context.Configuration.GetConnectionString(“DefaultConnection”)));
in IdentityHostingStartup.cs
Cancel Button is not working ..sir.
Hi Htoo
In the Manage View Page the a tag should be
Cancel
This will take you back to the Manage Index page.
🙂
Is this what you mean?
Cancel
in the manage View, you should change the
asp-action=”EditUser” to asp-action=”Index”
in the cancel button and it will work.
This is excellent. Thank you for your contributions.
just what i needed, however i followed the code a few times and dont seem to get the roles in Identity.role table
i don’t seem to be getting the default role showing in the identity.roles table
thanks
Hi, Can i get more details? Did you try debugging the code?
hi Mukesh
How can add permission for user (create,update,delete and view) for different page
Such an approach will be implemented soon. I Will let you know. You can otherwise work with Claims to check if a particular user identity has the required claims.
thanks for reply and i witting for you
also how can i contact with u
Hi, what a nice and in-depth article. I am working on a Razor application, and so far I have been able to apply all the concepts in my application. However, I can’t seem to get the “Addings Users to Roles” to work. Do you by any means know how to apply this in razor? Thanks
Thanks for nice article Mukesh
I check you hero boiletplate.
there is permission level of user role.
Can you show how does it work?
Hi, Sure. I will be posting an article regarding it this week. Will notify you when the article is up.
Regards.
Thanks for this article man. It helps a lot.
😀👍
I created a users list to edit. How can I call the same views like click on logged username?
Very thorough tutorial! Thank you very much!
I am having an issue with the database which is always adding a Identity.AspNetUsers table in addition to the other (renamed) tables.
This table is where the data goes when a user registers. It appears that this table is generated when a user registers if it is not present.
Do you have any insight on this problem.
Thanks,!
Hello,
This tutorial looks quite useful but I think you should give more information about paths hwere to insert code. For example, I’m at the point where you ask me to insert an Enum. But where? In the models folder? In an existing class?
Keep up the good work and I’m waiting for your answer.
Update: I found a solution and I’m grateful to have this tutorial.
Explain your problem and shared it’s solution. Maybe someone will find it useful.
S/he probably ran into a brain-freeze like I did.. until I (had coffee, and) realised that I needed to *create a new Enums.cs class under the Data folder*
In order to search for all*** occurrences of IdentityUser and replaced with Application user: press CTRL + , .
*** It’s not perfect, since it got me some troubles, described below.
If you find the following error:
No service for type ‘Microsoft.AspNetCore.Identity.UserManager`1[Microsoft.AspNetCore.Identity.IdentityUser]’ has been registered
In means one file is asking for a service of type IdentityUser instead of ApplicationUser.
So you have to search throughtout the files, and see which miss the ApplicationUser replacement.
In my case was a partial view: _ManageNav.cshtml. But other users have found that it can also occured with: _LoginPartial.cshtml
You can find more information here:
https://stackoverflow.com/questions/52568264/no-service-for-type-microsoft-aspnetcore-identity-usermanager1microsoft-aspne
On the _ManageNav.cshtml file make sure you have the following:
@using PROJECTNAME.Models
@using Microsoft.AspNetCore.Identity
@inject SignInManager SignInManager
Thanks Jorge.
Help me a lot.
in the tutorial, when modifying table names you have the code line 5 I believe, builder.Entity(entity =>…
in your code on Github it is (line21) builder.Entity(entity => …
Which is correct?
Another extremely helpful article! Thank you Mukesh!
Happy to help. Thanks for the feedback.
Regards
Mukesh, you forgot to mention in Adding Custom Fields To Identity User section :
Edit the following line of code inside OnModelCreating() method in ApplicationDbContext, :
From this
builder.Entity(entity =>
{
entity.ToTable(name: “User”);
});
to
builder.Entity(entity =>
{
entity.ToTable(name: “User”);
});
Because entity framework create a new Identity.AspNetUsers table instead of using Identity.User when we add a new migration .
You wrote the exact same text that should be replaced. Could you carify?
You wrote the exact same things.
Hi,
I think you want to say replace:
builder.Entity(entity =>
{
entity.ToTable(name: “User”);
});
to:
builder.Entity(entity =>
{
entity.ToTable(name: “User”);
});
Dude, you’re amazing for sharing this free content, i hope you get more attention, better than Udemy or YouTube tutorials.
Great app tutorial. Thanks!
I don’t think there was a need to replace IdentittyUser with ApplicationUser.
Great tutorial sir.
If I have another entity in the project such as car entity how can I create the table car without identity.car.
Thanks.
I can confirm that the table “Identity.AspNetUsers” still gets created, I believe it should not be created according with the instructions.
Your guide is excellent!!!
How do I get to manage user roles in .NET Core 5 and Razor pages? Do you have such a tutorial?
Hi, There is not really any difference that comes about with .NET 5. The same implementation is applicable for 5 as well.
Regards
Thank you so much. It was the best example, i was facing to many problems with this topic
That’s great to know. Thanks for the feedback though.
Regards.
Great tutorial, thank you so much!
You are welcome. Regards
ArgumentException: Value cannot be null or empty. (Parameter ‘normalizedRoleName’)
79. if (await _userManager.IsInRoleAsync(user, role.Name))
I get this exception while clicking on Manage Roles. What should I do?
Hi, I would suggest you check out your database table first, especially where you store the identity roles. Ensure the normalized role name column isn’t empty. If it’s empty, then the way you are inserting the roles to your table has an issue. That should be fixing it.
Regards
OMG l cant believe all clean and so good man thank you so much
I wish you were my teacher. Thanks a lot. Will buy you a coffee!
absolutley superb post. Thks.
Thank you so much for this. This helped me a lot. !!!!!
You are welcome 🙂 Regards
Hey,
what is the reason for the Cancel asp-action in the Manage.cshtml
Cancel
Nowhere is the “EditUser” definied.
This article is splendid. Unlike most other articles on the internet, the author has explained everything to the point very clearly. Thank you very much for your genuine effort.
Excellent Job.
Okay. I made the mistake of adding this to an existing project. A couple of things are not happening. First, when I tried to use my already existing Context class, the wizard would not continue. It forced me to create a new Context class. Second, no migration code was created.
Any ideas on what I need to do to rectify this?
This is a pretty slick tutorial. I’m still working my way through it, but it is very thorough and I think it is everything I need. Once I get past these initial issues, I think it’ll fly along.
I’ve been doing some digging, and there are some additional issues, and a possible reason. I created an app from scratch, and added all of the Identity classes. However, the dialog allowing me to add the source code never comes up. So I don’t see any code in the project. And in digging further into the original project that I modified, I noticed that none of the Identity Model classes were created. Which is why I think there was no migration code created. Makes sense.
Okay. The reason I think this is happening is that I am using Visual Studio 2019, Not Visual Studio Code. The dialogs in the creation wizard are distinctly different looking, and like I said–there is no opportunity to have the Identity source code added to the project.
I’m going to bite the bullet and download VS Code tonight and mess around. I’m hoping that I can get something hashed together. I’ll keep you posted. And if you have any insights or suggestions, please don’t hesitate to let me know.
I am using Visual Studio 2019 Community, and I am able to scaffold the identity classes without any issue. It is supposed to work out of the box.
Yeah. It’s not about VS Code.
I’m running VS 2019 Enterprise. The dialogs are definitely different looking. Very odd.
I’ve downloaded your source. I’m going to attack this from that angle, just so I can see the completed project and how it runs.
Okay. I took a couple days off for Mother’s Day and weekend stuff. I’m making progress. But I’m running into a table naming issue. When I generate the table update migration code, the following appears:
modelBuilder.Entity(“MapItGo_Web_App.Models.ApplicationUser”, b =>
{
b.Property(“Id”)
.HasColumnType(“nvarchar(450)”);
.
.
.
b.ToTable(“AspNetUsers”);
});
Where is “AspNetUsers” coming from? When I run the migration code, it creates an AspNetUsers table, in addition to the initial Users table. The only real thing I’m doing differently is I’ve got a different schema than the one you use in this article. But that can’t possibly be the reason for this to happen.
Any thoughts? I’ve combed through my code, and nothing is jumping out at me. I’ve been able to run the code and the login and register pages will run without crashing, so there’s that. 🙂
I am making progress. This is going to be really useful for my website. I really do appreciate the effort you put into this article.
I’ve figured most of it out. Thank you Google. 🙂
I took the services.AddDefaultIdentity… out of IdentityHostingStartup.
I’ve kept the AspNetUsers table. It’s the only way I can get it to run completely.
I added app.UseAuthentication() to Startup.Configure(…).
Those three things have me to the point where I can log in and see what’s expected in the menu bar. I can click on my username and get to the configuration side menu. Now I need to make the changes you’ve outlined after that.
I’ll keep you posted.
Yes
Hi Mukesh Murugan,
The imaga profil can not add to site.
Thanks Mukesh for such an enlightening tutorial, I followed the steps but I am getting an error on profile picture update, its saying
The value ‘xxx.png’ is not valid for Profile Picture.
Kindly assist.
Same with me. What would be the mssql data type for the profile picture? Thanks!
Did any of you manage to fix this?
This was exactly what I needed! Thanks so much for helping this code noob make users and roles much easier! You saved me many many headaches. Your coffees should be in you inbox 😀
Hi @Mukesh, thanks for this great tutorial!:) I have an issue thought during seeding…:/ It wont seed the roles! I am using VS 2019 and Asp.Net Core 5.0. The exception logger gets is the following :
{“No service for type ‘Microsoft.AspNetCore.Identity.RoleManager`1[Microsoft.AspNetCore.Identity.IdentityRole]’ has been registered.”} System.Exception {System.InvalidOperationException}
This is how my IdentityHostingStartup.cs looks like:
[assembly: HostingStartup(typeof(YouPod.Areas.Identity.IdentityHostingStartup))]
namespace YouPod.Areas.Identity
{
public class IdentityHostingStartup : IHostingStartup
{
public void Configure(IWebHostBuilder builder)
{
builder.ConfigureServices((context, services) =>
{
services.AddDbContext(options =>
options.UseSqlServer(
context.Configuration.GetConnectionString(“YouPodDbContextConnection”)));
services.AddDefaultIdentity(options => options.SignIn.RequireConfirmedAccount = false) //false for now and when I will configure SendGrid this should change to true
.AddEntityFrameworkStores();
});
}
}
}
and my program.cs is exactly like yours. Could I please ask for your input? Thanks!
Hi,
Please refer to https://github.com/fluentpos/fluentpos/blob/master/src/server/Modules/Identity/Modules.Identity.Infrastructure/Extensions/ServiceCollectionExtensions.cs
I suppose you are not adding IdentityRole into the service container.`
You would need something like
`.AddIdentity
Hi Mukesh
wonderful tutorial, i got through most of it , only one error i am not able to resolve at the moment.
if you come across the solution do let me know..
when iam trying to modify roles i get below exception…
https://localhost:5001/UserRoles/Manage?userId=8ebb29e4-2d2e-46cd-a704-072cbc5a80b4
An unhandled exception occurred while processing the request.
InvalidOperationException: This MySqlConnection is already in use. See https://fl.vu/mysql-conn-reuse
MySqlConnector.Core.ServerSession.StartQuerying(ICancellableCommand command) in ServerSession.cs, line 281
hi Murkesh
i got it fixed, its the
foreach (var role in _roleManager.Roles) >>>> is changed to
foreach (var role in _roleManager.Roles.ToList())
I got this too – I had to go into Areas/Identity/Pages/Account/Manage/_ManageNav.cshtml and change to . There was one more place I had to do that too in Views/Shared/_LoginPartial – same thing – to . Hope that helps!
https://www.youtube.com/watch?v=6h21fvX3nKg
you can find the solve in the url
Hello Mukesh
Thank you for your effort to write this amazing Tutorial.
I have a Question:
I can’t close any StatusMessage.
I’ve downloaded your Project and it works but on my Project something is wrong.
What i’ve tried is replacing content of this sites but with no success;
_StatusMessage.cshtml -> Areas\Identity\Pages\Account\Manage\
_StatusMessage.cshtml -> Areas\Identity\Pages\Account\
Index.cshtml -> Areas\Identity\Pages\Account\Manage\
Index.cshtml.cs -> Areas\Identity\Pages\Account\Manage\
Also im a beginner. do you have any suggestions?
I’ve updated my project to Bootstrap5 could this perhaps be the Problem?
Thank you again for this great Tutorial it helped me a lot.
Awesome post. By far the most simplistic explanation of the concept. Very Helpful.
thank you very much
this is the best document about Identity
Thanks you. Your code is amazing. I have a minor suggestion for your code.
In Controller UserRole’s action Manage,
foreach (var role in _roleManager.Roles) // change to foreach (var role in await _roleManager.Roles.ListAsnyc() )
{
. . .
}
Or it will raise an exception while running under Asp.Net Core 5. (ps: Does Asp.Net core 3 accept it ? )
Best way
await _roleManager.Roles.AsNoTracking().ToListAsync()
Awesome post
https://stackoverflow.com/questions/52522248/store-does-not-implement-iuserrolestoretuser-asp-net-core-identity
same thing and update the connection string to
context.Configuration.GetConnectionString(“DefaultConnection”)));
in IdentityHostingStartup.cs
Hello Mister. excellent tutorial, thanks for sharing it. I got stock trying to Login with the default user credentials. When I put on Username box superadmin, it says that is not valid
//Seed Default User
var defaultUser = new ApplicationUser
{
UserName = “superadmin”,
Email = “pruebasGuti_91@bussinessguti.com”,
FirstName = “Herbert”,
LastName = “Gutierrez”,
EmailConfirmed = true,
PhoneNumberConfirmed = false
};
if (userManager.Users.All(u => u.Id != defaultUser.Id))
{
var user = await userManager.FindByEmailAsync(defaultUser.Email);
if (user == null)
{
await userManager.CreateAsync(defaultUser, “1234567.Guti”);
await userManager.AddToRoleAsync(defaultUser, Enums.Roles.Basic.ToString());
await userManager.AddToRoleAsync(defaultUser, Enums.Roles.Moderator.ToString());
await userManager.AddToRoleAsync(defaultUser, Enums.Roles.Admin.ToString());
await userManager.AddToRoleAsync(defaultUser, Enums.Roles.SuperAdmin.ToString());
}
}
Thanks for awesome tutorial. I got stocked trying to login with the default user’s username “superadmin”, and it says that is not valid. Am I doing something wrong ??? Or should I change it from my profile ??
var defaultUser = new ApplicationUser
{
UserName = “superadmin”,
Email = “pruebasGuti_91@bussinessguti.com”,
FirstName = “Herbert”,
LastName = “Gutierrez”,
EmailConfirmed = true,
PhoneNumberConfirmed = false
};
if (userManager.Users.All(u => u.Id != defaultUser.Id))
{
var user = await userManager.FindByEmailAsync(defaultUser.Email);
if (user == null)
{
await userManager.CreateAsync(defaultUser, “1234567.Guti”);
await userManager.AddToRoleAsync(defaultUser, Enums.Roles.Basic.ToString());
await userManager.AddToRoleAsync(defaultUser, Enums.Roles.Moderator.ToString());
await userManager.AddToRoleAsync(defaultUser, Enums.Roles.Admin.ToString());
await userManager.AddToRoleAsync(defaultUser, Enums.Roles.SuperAdmin.ToString());
}
}
The username generating logic here in this code is from the email. i.e if email is pruebasGuti_91@bussinessguti.com, then username should be pruebasGuti_91 . But you assigned username different. I hope changing the username to that of email (before @) works.
Mukesh, thank you so much for a detailed step-by-step explanation and clear guidance without any unnecessary BS. You saved me a loooot of time, thanks again and the best wishes!
Hello, I have having an error whenever I am opening the page, although I can close this error and still register and data will still be stored. But every time I navigate through pages this error is always showing.
Unhandled Rejectiaon (Error): Could not load settings for ‘Application.Web’ AuthorizeService.ensureUserManagerInitialized
Anyone can help me out. Kindly acknowledge my question, please. Thank you.
Hello, it’s odd that the code works to others, but in my case it does not.
[HttpPost]
public async Task AddRole(string roleName)
{
if (roleName != null){
await _roleManager.CreateAsync(new IdentityRole(roleName.Trim()));
}
return RedirectToAction(“Index”);
}
Hello, on my end, this does not work. Can you help me please? thank you
I got the same problem as you.
Please check the Index.cshtml view and if you adding the line:
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
at the top of the view it will work.
And I am wondering why I need to add this line at each view when I have added it to _viewimports.cshtml.
And I have asked Muskesh about this.
hi i follow the step by step instructions , user registration is ok but when i do the login it says
Invalid login attempt.
i am using .Net core 3.1
thank you very much for the great work , in fact i know many things here long time i tried to obtain but now it is ok ,,, thanks again sir.
Adding the override function to the ApplicationDbContext.cs file gives me errors.
“A namespace cannot directly contain members such as fields or members”
“no suitable method found to override”
Nice Article Mr Mukesh
thank you very much
could you please add delete role?
Thanks Mukesh – managed to complete the “tutorial” and found it an excellent intro to .Net Core Identity
var roleManager = services.GetRequiredService<RoleManager>();
Error
No service for type ‘Microsoft.AspNetCore.Identity.RoleManager`1[Microsoft.AspNetCore.Identity.IdentityRole]’ has been registered
thanks you.
But, It have a issue.
‘OnModelCreating method’ in ApplicationDbContext.cs
if you want to change the table name from database. you must try change.
✅From
builder.Entity(entity =>
{
entity.ToTable(name: “User”);
});
✅to
builder.Entity(entity =>
{
entity.ToTable(name: “User”);
});
✅From
“`c#
builder.Entity(entity =>
{
entity.ToTable(name: “User”);
});
“`
✅to
“`c#
builder.Entity(entity =>
{
entity.ToTable(name: “User”);
});
“`
✅From
builder.Entity(entity =>
{
entity.ToTable(name: “User”);
});
✅to
builder.Entity(entity =>
{
entity.ToTable(name: “User”);
});
Hi Mukesh,
One of the best blog I’ll say..it helps me lot to understand the new stuff and implement it in my upcoming solutions.
Can you please suggest some better way to prevent multiple & concurrent login in .Net Core?
Thanks.
About section “40+ Identity pages” and “But even after that you would have to manually go to each page to add the namespace reference”. When upgrade to .NET 6, you can solve this issue by using the new global usings feature of C# 10. For example: just add GlobalUsings.cs with this line: global using UserManagement.MVC.Models;
Thanks for your awesome blog!
You did THE EXACT same changes to the Identity naming I did! And yes… it looks much better!J
Hi Mukesh,
Thanks for the excellent tutorial for using Identity and Roles.
I have a small issue. When trying to Manage User Roles by clicking on Manage button https:///UserRoles/Manage, I get InvalidOperationException: The view ‘NotFound’ was not found. The following locations were searched: /Views/UserRoles/NotFound.cshtml /Views/Shared/NotFound.cshtml /Pages/Shared/NotFound.cshtml
Can you point me towards resolution?
Thanks
Please tell me how can use seed data in asp.net 6 web application?
The steps would be same in .NET 6 as well. Are you facing any issues?
Hi, Mukesh.
Very good tutorial.
I’m arriving to Blazor. I’m creating an app with the server option.
Instead of having Views and Controllers, I would like to have the same funcionality with razor components.
Can you guide me? Is it something feasible?
Regards,
Fausto
Hi,
Thank for the detailed tutorial about user management in ASP.NET Core.
But when I try to add the Firstname and Lastname to the database, the code always create the User table but also the old table AspNetUsers. I tried this in VS2019 and VS2022 Preview, both Visual Studios have their latest updates installed.
The only solution have found is manually change the code in de migration file.
Have you an idea how to fix this or what the problem would be?
Thanks in advanced.
I guess you are not extending the IdentityUser class. Did you inherit you User class from the IdentityUser class? That would be the only reason 2 tables are getting created for you.
Thanks
Thanks for the replay.
Yes I did. I put the code of my ApplicationUser class below.
public class ApplicationUser : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string NickName { get; set; }
} // end
Best regards.
Danny
You need to replace “ApplicationUser” for “IdentityUser” in “OnModelCreating”:
protected override void OnModelCreating(ModelBuilder builder)
{
.
.
builder.Entity(entity =>
{
entity.ToTable(name: “User”);
});
.
.
.
}
Great tutorial, thanks very much!!!!
Hi, i have put credentials in fields and clicked register button, and i works, it even redirects to home page, but when i look in database in table Identity.User, there are no data in there.
In the startup.cs file
services.AddDefaultIdentity(options =>
options.SignIn.RequireConfirmedAccount = true;
)
here if i change this false then i can register otherwise it is showing error.
After register I can’t login . Although all information saved in database.
What’s the issue?
Very good tutorial.
Thanks a lot, Mukesh
Great content. Can you also do LDAP authentication in latest aspnet mvc.
Hi
public async Task Manage(string userId)
{
ViewBag.userId = userId;
var user = await _userManager.FindByIdAsync(userId);
if (user == null)
{
ViewBag.ErrorMessage = $”L’utilisateur avec l’identifiant = {userId} est introuvable”;
return View(“NotFound”);
}
ViewBag.UserName = user.UserName;
var model = new List();
foreach (var role in _roleManager.Roles.ToList())
{
var userRolesViewModel = new ManageUserRolesViewModel
{
RoleId = role.Id,
RoleName = role.Name
};
Error in
await _userManager.IsInRoleAsync(user, role.Name)
An unhandled exception occurred while processing the request.
NotSupportedException: Store does not implement IUserRoleStore.
Can you help me thanks
Hello,
everything works perfectly.
I’ve been trying to sort a list of users by roles. Any ideas ?
Thanks Mukesh for such an enlightening tutorial, I followed the steps but I am getting an error on profile picture update, its saying
The value ‘xxx.png’ is not valid for Profile Picture.
Kindly assist.
Hello, I can not find link to download the sample code.Can you send me the link please? Thanks!
The link to my repository is already added a couple of times throughout this article. Anyways, here is it for you – https://github.com/iammukeshm/CustomUserManagement.MVC
Hi Mukesh, I’m working with Asp .Net 6 and I can’t invoke the ContextSeed Methods with program.cs. In this new version 6 StartUp no longer exists. Please help me to solve. Thank you!
Hi
I did this tutorial in .net core and was brilliant. Now redoing in .net6 and hitting the same obstacles as everyone else.
I put this in Program.cs….
//create roles
using (var scope = app.Services.CreateScope())
{
var services = scope.ServiceProvider;
ContextSeed.Initialize(services);
}
I put this in Enums\Roles.cs….
namespace Cxtras.Enums
{
public enum Roles
{
Admin,
SiteAdmin,
SiteUser
}
}
and this in ContextSeed.cs….
using Cxtras.Enums;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
namespace Cxtras.Data
{
public static class ContextSeed
{
public static void Initialize(IServiceProvider serviceProvider)
{
using (var context = new ApplicationDbContext(serviceProvider.GetRequiredService<DbContextOptions>()))
{
var roles = Enum.GetValues(typeof(Cxtras.Enums.Roles));
var newrolelist = new List();
foreach (string role in Enum.GetNames(typeof(Roles)))
{
if (!context.Roles.Any(R => R.Name == role))
{
newrolelist.Add(new IdentityRole(role));
}
}
context.Roles.AddRange(newrolelist);
context.SaveChanges();
}
}
}
}
I’m not sure its perfect coding but its worked!
Jim
This is an excellent tutorial. I already had some experience with C# and .NET and wanted to discover more about Identity and Authorization and this has helped greatly, many thanks. Keep up the good work – it is very well explained and concise.
Thank you very much,
You help me a lot.
I learn from this tutorial to make my ASP.NET Core 6 Razor Pages Apps.
Hi! I just wanted to ask how would you route each role to a specific landing page. Like a basic user should not be able to access administrator functions.
All the best!
Sir your skipping this step
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity()
.Property(e => e.firstName)
.HasMaxLength(250);
modelBuilder.Entity()
.Property(e => e.lastName)
.HasMaxLength(250);
// modelBuilder.Entity()
//.Property(e => e.ProfilePicture)
// .HasMaxLength(250);
}
This is a VERY HELPFUL blog on Identity!!! THANK YOU.
I did find with using ASP.NET Core Razor Pages that I had to add the following code to get the _userManager to work:
private readonly UserManager _userManager;
I inserted it just after the “public class LoginModel : PageModel” definition. I hope this helps other developers.
Hello Mukesh,
thank you for your great post. I tried your approach to change identity table names and tested it with a sample project.
I manged to change tables names and to add additional properties to the ApplicationUser. So far so good.
Now I tried to change the identity key for the ApplicationUser to an integer. I have successfully done this in a project with original identity tables names and used the
approach outlined at https://robertwray.co.uk/blog/taking-the-guid-out-of-asp-net-core-identity.
So I tried the same approach with the sample project where I had changed the identity tables names. But applying the according migration from the package manager console gives an error:
***************** error message *******************************
The entity type ‘IdentityUserLogin’ requires a primary key to be defined. If you intended to use a keyless entity type, call ‘HasNoKey’ in ‘OnModelCreating’. For more information on keyless entity types, see https://go.microsoft.com/fwlink/?linkid=2141943.
*****************************************************************
Now I am stuck and I wonder if you also have tried changing the identity key and could give me advise how to fix the error or point me to some documentation that could help.
Regards, Manfred
Excellent! Work correctly.
How to Add FirstName,LastName MVC_core 6?
there is no ApplicationUser() in OnPostAsync() Action..
Where can i access the Source-Code ?
Please refer to https://github.com/iammukeshm/CustomUserManagement.MVC
Hi Mukesh,
Excellent explanation. Do you have some blog on non core Asp.Net Identity MVC too?
hi Mukesh
great tutorial, so that i have a problem
i have a problem in a part when change the name of tables
Hi Mukesh,
How to add Login Session and Logout Session in this .Netcore Identity that needs to store database? Any idea
Great work,
it is really helpful Mukesh.
For those who have a problem with .Net core 6 or 7 for seeding, as Jim Walker suggested with some modification:-
Program.cs
create roles
using (var scope = app.Services.CreateScope())
{
var services = scope.ServiceProvider;
//ContextSeed.Initialize(services);
var context = services.GetRequiredService();
var userManager = services.GetRequiredService<UserManager>();
var roleManager = services.GetRequiredService<RoleManager>();
await ContextSeed.SeedRolesAsync(userManager, roleManager);
await ContextSeed.SeedSuperAdminAsync(userManager, roleManager);
}
ContextSeed.cs
public static class ContextSeed
{
public static async Task SeedRolesAsync(UserManager userManager, RoleManager roleManager)
{
//Seed Roles
await roleManager.CreateAsync(new IdentityRole(Enums.Roles.SuperAdmin.ToString()));
await roleManager.CreateAsync(new IdentityRole(Enums.Roles.Admin.ToString()));
await roleManager.CreateAsync(new IdentityRole(Enums.Roles.Moderator.ToString()));
await roleManager.CreateAsync(new IdentityRole(Enums.Roles.Basic.ToString()));
}
public static async Task SeedSuperAdminAsync(UserManager userManager, RoleManager roleManager)
{
//Seed Default User
var defaultUser = new ApplicationUser
{
UserName = “superadmin”,
Email = “superadmin@gmail.com”,
FirstName = “Mukesh”,
LastName = “Murugan”,
EmailConfirmed = true,
PhoneNumberConfirmed = true
};
if (userManager.Users.All(u => u.Id != defaultUser.Id))
{
var user = await userManager.FindByEmailAsync(defaultUser.Email);
if (user == null)
{
await userManager.CreateAsync(defaultUser, “123Pa$$word.”);
await userManager.AddToRoleAsync(defaultUser, Enums.Roles.Basic.ToString());
await userManager.AddToRoleAsync(defaultUser, Enums.Roles.Moderator.ToString());
await userManager.AddToRoleAsync(defaultUser, Enums.Roles.Admin.ToString());
await userManager.AddToRoleAsync(defaultUser, Enums.Roles.SuperAdmin.ToString());
}
}
}
Hi Mukesh, thanks again for the great description!
The instructions helped me a lot with an internal app. Everything runs without errors.
I programmed a customer application using the same technique. Basically everything works (menu, login/logout, role authorization, etc.). However, there is an effect on the customer that I have not yet been able to explain:
I have a link from the record (Example: “https://domainxy/Test/Edit/7”). If I am previously logged out, this link always opens the “https://domainxy/Identity/Account/Login?ReturnUrl=%2FTest%2FEdit%2F7” page in my development environment. For the customer, however, “https://domainxy/Identity/Account/AccessDenied?ReturnUrl=%2FTest%2FEdit%2F7” always opens.
What could be the cause?
Where can I find the place in the code that redirects to AccessDenied.cshtml?
Best regards
Good, I went ahead and added it like this (it would’ve saved me a lot of time to look at your comment before) after var app = builder.Build();
using (var scope = app.Services.CreateScope())
{
var userManager = scope.ServiceProvider.GetService< UserManager>();
var roleManager = scope.ServiceProvider.GetService< RoleManager>();
await ContextSeed.SeedRolesAsync(userManager, roleManager);
await ContextSeed.SeedSuperAdminAsync(userManager, roleManager);
}
Thank you for the amazing tutorial.
If you don’t mind, one small suggestion would be adding a Roles Page rather than navigating by typing the /UserRoles in the URL.
Overall really good tutorial.
Thank you so much for this article, helped me in so much ways!
Hello Mukesh, it’s an excellent article. Thanks a lot.
Hey
thx for sharing
Do you have somewhere a sample project with row level security ?
I’d like to implement this into BlazorHero
Thx
Hi, thanks so much for your article! I’d like to ask if there is any way to customize a page AFTER it scaffolds. My friend did not include some of the pages during scaffolding, such as Register.cshtml, and now we cannot edit that page. Can you advise? Thank you so much.
To hide the Role access for none-SuperAdmin
@if (SignInManager.IsSignedIn(User) && User.IsInRole(“SuperAdmin”))
{
User Roles
Role Manager
}
Hi. Can you use this with .NET Core 6 MVC? And can you use it with existing users, roles,
Yes, you can use it. I used a different article that this article is copied from and added little extra stuff. And with some brain behind it I was able to do it.
But this article will help you.
Mukesh, this is useful tutorial. I have downloaded your source code via GitHub and I think IEmailSender Interface was not part of the source code solution. Would you be able to provide reference to it?
You saved me about 3 weeks worth of work with this tutorial. A heartfelt thanks.
Considering the amount of ground you covered, I am amazed at how little tweaking I had to do to get it all to work. And I did it in core 7.
I will be looking at your tutorials first whenever I try to do anything.
Again, thanks!!!
Very good tutorial.
Thanks a lot, Mukesh
For some reason i was receiving SQL null errors when user was authenticated
Fixed by changing strings to “nullable” in APplicationUsers.cs, for example:
public string? FirstName { get; set; }
public string? LastName { get; set; }
added new migration and updated database
Hi, After adding enums and contextSeed with required services.
This is showing error like:
An error occurred seeding the DB.
System.InvalidOperationException: Cannot resolve scoped service ‘UserRoles.Data.ApplicationDbContext’ from root provider.
How to resolve this error?