Skip to main content
SEMastery
Data Accessintermediate

How to Customize ASP.NET Core Identity With EF Core for Your Project Needs

Learn to customize ASP.NET Core Identity with EF Core: add user fields, change the key type, extend roles, and run migrations safely on .NET 10.

11 min readUpdated February 20, 2026

How to Customize ASP.NET Core Identity With EF Core for Your Project Needs

Think about getting a new school uniform. The shop gives everyone the same basic shirt. But your school wants a badge on the pocket, your name on the collar, and a house color on the sleeve. The shirt is a good start. You just add your own bits to make it fit your school.

ASP.NET Core Identity is exactly like that basic shirt. It already knows how to store users, hash passwords, handle roles, and log people in. That is a lot of hard work done for you. But every real project needs a few extra bits. Maybe you want a FullName, a DateOfBirth, or a user Id that is a number instead of a long text string.

The good news is that Identity was built to be changed. You do not throw it away. You take the strong base it gives you and you stitch your own fields onto it. In this post you will learn how to do that step by step, in plain words, using Entity Framework Core to save everything in your database.

We will use .NET 10, which is the current long-term support (LTS) release, and C# 14. If you know a little C# and a little EF Core, you are ready.

What you start with

When you turn on Identity, it creates a set of tables for you. The main one is the users table. Out of the box, a user already has fields like a unique Id, an Email, a UserName, and a hashed password. Here are the tables Identity makes by default.

TableWhat it holds
AspNetUsersOne row per user, with email and password hash
AspNetRolesThe roles, like Admin or Member
AspNetUserRolesLinks a user to the roles they have
AspNetUserClaimsExtra facts about a user (claims)
AspNetUserLoginsExternal logins, like Google sign-in
AspNetUserTokensTokens, such as for password reset

This is plenty for a simple app. The trouble starts when your boss says, "We also need each user's full name and their joining date." Those fields are not there yet. That is where customizing comes in.

Identity gives you a base user; you extend it with your own fields.

Step 1: Make your own user class

The first and most common change is adding fields to the user. You do this by making a new class that inherits from IdentityUser. Inheriting means "take everything the base has, then add more."

using Microsoft.AspNetCore.Identity;
 
public class ApplicationUser : IdentityUser
{
    public string FullName { get; set; } = string.Empty;
 
    public string? JobTitle { get; set; }
 
    public DateOnly JoinedOn { get; set; }
}

That is it for the class. ApplicationUser now has the email, the password hash, the Id, and everything else from IdentityUser, plus your three new fields. Notice we use DateOnly for the joining date because a join date does not need a time of day. We also marked JobTitle as nullable with ? because a new user might not have one yet.

Step 2: Tell the DbContext about your user

Identity stores data through a special DbContext called IdentityDbContext. You must tell it to use your user class instead of the plain one. You do that by passing your class inside the angle brackets.

using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
 
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }
 
    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);
 
        // Always call base first so Identity sets up its own tables.
        builder.Entity<ApplicationUser>(b =>
        {
            b.Property(u => u.FullName).HasMaxLength(200);
            b.Property(u => u.JobTitle).HasMaxLength(100);
        });
    }
}

There is one rule you must never forget here: always call base.OnModelCreating(builder) first. Identity uses that call to build all of its own tables and links. If you skip it, your login system will break in confusing ways. After the base call, you can add your own tweaks, like setting a max length so the database column is not wastefully huge.

Step 3: Register Identity in Program.cs

Now you wire it all together in Program.cs. You tell the app which user class, which roles, and which database to use.

var builder = WebApplication.CreateBuilder(args);
 
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(
        builder.Configuration.GetConnectionString("Default")));
 
builder.Services
    .AddIdentity<ApplicationUser, IdentityRole>()
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddDefaultTokenProviders();
 
var app = builder.Build();

Read this slowly. AddIdentity<ApplicationUser, IdentityRole>() says "my users are ApplicationUser, my roles are the normal IdentityRole." AddEntityFrameworkStores<ApplicationDbContext>() says "save all of this using my DbContext." AddDefaultTokenProviders() switches on the bits that make password-reset and email-confirm tokens.

Here is the whole flow from your class to a real database table.

From a C# property to a database column

Add field
Update DbContext
Add migration
Update database

Steps

1

Add field

Put FullName on your user class

2

Update DbContext

Use IdentityDbContext of your user

3

Add migration

EF writes the change script

4

Update database

The new column appears

The four steps that turn your custom field into a real column

Step 4: Add a migration and update the database

EF Core does not change your database by magic. You must create a migration, which is a small file describing the change, and then apply it. Run these two commands in your project folder.

dotnet ef migrations add AddCustomUserFields
dotnet ef database update

The first command looks at your model, sees the new FullName, JobTitle, and JoinedOn fields, and writes a script. The second command runs that script against your real database, adding the new columns to the AspNetUsers table. After this, you can save and read those fields like any normal EF Core property.

The migration is the bridge between your code and the database.

Changing the primary key type

By default, the user Id is a string that holds a GUID written as text. That works fine, but some teams prefer a real Guid type, or an int, for smaller and faster keys. You can change this, but it is a bigger job.

To use a Guid, your user class must inherit from IdentityUser<Guid>, and your roles from IdentityRole<Guid>. The DbContext must also be told the key type.

public class ApplicationUser : IdentityUser<Guid>
{
    public string FullName { get; set; } = string.Empty;
}
 
public class ApplicationRole : IdentityRole<Guid>
{
}
 
public class ApplicationDbContext
    : IdentityDbContext<ApplicationUser, ApplicationRole, Guid>
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }
}

The key type flows through every related table, so the user-roles, claims, and tokens tables all switch their key column too. Because this changes so many tables, do it before you have real users in production. Changing the key type after launch means migrating every existing row, which is risky.

Here is a quick guide on which base class to pick.

Your needUser base classDbContext base class
Just add fieldsIdentityUserIdentityDbContext<TUser>
Add fields + custom roleIdentityUser, IdentityRoleIdentityDbContext<TUser, TRole, string>
Change key to GuidIdentityUser<Guid>IdentityDbContext<TUser, TRole, Guid>
Change key to intIdentityUser<int>IdentityDbContext<TUser, TRole, int>

Extending roles and other tables

You are not limited to the user. You can add fields to roles too. Say each role should have a friendly Description. Make an ApplicationRole that inherits from IdentityRole, add the field, and feed it to the DbContext just like you did for the user.

public class ApplicationRole : IdentityRole
{
    public string? Description { get; set; }
}

The same idea works for the other Identity tables, such as user claims or user tokens. You make your own class that inherits from the matching Identity type, add your fields, and list every type in the long form of IdentityDbContext. Most projects only customize the user and sometimes the role, so you rarely need the others.

This diagram shows how all the custom classes relate to the base Identity classes.

Each custom class inherits from a matching Identity base class.

Reading and writing your custom fields

Once your fields exist, you use them through UserManager<ApplicationUser>, which Identity gives you through dependency injection. Here is a small example that creates a user and fills in the custom fields.

public class SignUpService
{
    private readonly UserManager<ApplicationUser> _userManager;
 
    public SignUpService(UserManager<ApplicationUser> userManager)
    {
        _userManager = userManager;
    }
 
    public async Task<IdentityResult> RegisterAsync(string email, string fullName)
    {
        var user = new ApplicationUser
        {
            UserName = email,
            Email = email,
            FullName = fullName,
            JoinedOn = DateOnly.FromDateTime(DateTime.UtcNow)
        };
 
        return await _userManager.CreateAsync(user, "Strong#Pass123");
    }
}

Because FullName and JoinedOn are normal EF Core properties, UserManager.CreateAsync saves them along with everything else. To read them later, fetch the user with await _userManager.FindByEmailAsync(email) and your fields are right there on the object.

Here is the journey of one sign-up request, from button click to a saved row.

A sign-up request with custom fields

Form
Controller
UserManager
EF Core
Database

Steps

1

Form

User types name and email

2

Controller

Builds ApplicationUser

3

UserManager

CreateAsync hashes password

4

EF Core

INSERT with custom fields

5

Database

Row saved in AspNetUsers

How your extra fields travel from the form to the database

Common mistakes to avoid

A few small slips trip up almost everyone. Knowing them ahead of time saves you hours.

Forgetting base.OnModelCreating. As said earlier, if you override OnModelCreating and forget to call the base method first, Identity cannot set up its tables. Always call base, then add your own lines.

Mismatched key types. If your user uses Guid but your DbContext still says string, the app will not even start. The key type must match in the user class, the role class, and the DbContext.

Forgetting the migration. Adding a property in C# does nothing to the database until you add and apply a migration. If your new column "is missing," you almost always forgot one of the two dotnet ef commands.

Renaming the DbSet on Users. Identity expects its own table names. If you must rename them, do it through OnModelCreating with ToTable, not by guessing. Keep the change in one place.

When to customize versus when to leave it

Not every extra fact about a user belongs on the user table. If a field is small, always present, and you query it often (like FullName), put it on the user class. If a field is rare, changes a lot, or is a list (like a user's many addresses), make a separate table with a foreign key to the user, instead of stuffing it into Identity. Identity stays clean, and your queries stay fast. You can also store occasional facts as claims when they are about permission or identity, such as a department code.

Quick recap

  • ASP.NET Core Identity is a strong base. You extend it, you do not replace it.
  • To add fields, make a class that inherits from IdentityUser and add your properties.
  • Tell IdentityDbContext<ApplicationUser> to use your class, and always call base.OnModelCreating first.
  • Register everything in Program.cs with AddIdentity, AddEntityFrameworkStores, and AddDefaultTokenProviders.
  • Run dotnet ef migrations add and dotnet ef database update so the new columns are really created.
  • To change the key type to Guid or int, inherit from IdentityUser<TKey> and match the type in the DbContext. Do this early.
  • You can extend roles and other tables the same way, but most apps only touch the user and role.
  • Read and write your fields through UserManager<ApplicationUser> like any normal property.
  • Keep large, rare, or list-like data in separate tables, not on the user.

References and further reading

Related Posts