In this article, we are going to learn about extending IdentityUser with custom properties in ASP.NET Core Identity.
Let’s start.
Extending IdentityUser
We will start with a default project that already implements identity. To learn more about ASP.NET Core Identity implementation, check out our article series on the topic.
To add our custom properties to the default IdentityUser
, let’s create a class that inherits from it:
public class ApplicationUser : IdentityUser { }
From now on, we should use ApplicationUser
everywhere in our code instead of IdentityUser
.
If we take a look at the IdentityUser
implementation, we can see that it inherits from the generic IdentityUser<TKey>
class and uses string
as the type parameter. If we inspect its primary constructor we will see that it uses a Guid
as Id
:
public IdentityUser() { Id = Guid.NewGuid().ToString(); SecurityStamp = Guid.NewGuid().ToString(); }
Changing the Primary Key Type on IdentityUser
Now, let’s change the key type to a real Guid
so that in the SQL table generated, it can be of type uniqueidentifier
, ensuring uniqueness at the database level.
To change the type of the Id
property, let’s make our ApplicationUser
class inherit from the generic IdentityUser<TKey>
and pass the Guid
as the type parameter:
public class ApplicationUser : IdentityUser<Guid> { }
This is the only thing that we have to change in our domain model, however, we are not ready yet.
Updating the Infrastructure Code
Once that is done, we should update the ApplicationDbContext
and the dependency injection registration to be able to utilize our ApplicationUser
class.
Firstly, if we inspect the IdentityDbContext
class, we will notice that it has many generic parameters. It’s important to note, that by changing the primary key type of the ApplicationUser
, we essentially have to change all other identity-related primary key types too.
Let’s inspect the IdentityDbContext
class:
public class IdentityDbContext<TUser, TRole, TKey> : IdentityDbContext<TUser, TRole, TKey, IdentityUserClaim<TKey>, IdentityUserRole<TKey>, IdentityUserLogin<TKey>, IdentityRoleClaim<TKey>, IdentityUserToken<TKey>> where TUser : IdentityUser<TKey> where TRole : IdentityRole<TKey> where TKey : IEquatable<TKey> { public IdentityDbContext(DbContextOptions options) : base(options) { } protected IdentityDbContext() { } }
It has three generic parameters, the first two are the TUser
and TRole
parameters and the third is the TKey
parameter. From the generic parameter constraints we can see, that both TUser
and TRole
types must have the same TKey
type. Moreover, all the other identity-related classes, such as IdentityUserClaim<TKey>
or IdentityUserRole<TKey>
will use the same TKey
as our ApplicationUser
class. So all in all, changing the ApplicationUser
class to have a Guid
id we made everything else have a Guid
id too.
Now, let’s change our ApplicationDbContext
to inherit from the correct IdentityDbContext
:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser, IdentityRole<Guid>, Guid> { public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { } }
We pass the ApplicationUser
class as the TUser
parameter. Because we don’t plan on extending roles the way we did with users, we pass the default IdentityRole
class with Guid
type parameter as TRole
. And finally, we specify TKey
as Guid
too.
As the last step, let’s configure the dependency injection registration of ASP.NET Core Identity:
builder.Services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true) .AddEntityFrameworkStores<ApplicationDbContext>();
We change the type parameter of the AddDefaultIdentity()
method to use our ApplicationUser
class. This way, UserManager
will be able to work with our custom user class.
Adding and Configuring Custom Properties to IdentityUser
With the infrastructure configured, let’s proceed by adding some custom properties to our user class.
From now on, adding and configuring custom properties is almost the same process as when we add them to any other entity. Let’s start with some primitive properties.
Custom Primitive Properties on IdentityUser
Let’s add two primitive custom properties to the ApplicationUser
class:
public class ApplicationUser : IdentityUser<Guid> { public string DisplayName { get; set; } public DateTime LastLoginDateTime { get; set; } }
Here, we add the DisplayName
and the LastLoginDateTime
properties to our class. Now, let’s see how to apply database constraints to the DisplayName
property.
Let’s override the OnModelCreating()
method of the ApplicationDbContext
:
protected override void OnModelCreating(ModelBuilder builder) { base.OnModelCreating(builder); builder.Entity<ApplicationUser>(b => { b.Property(u => u.DisplayName).IsRequired().HasMaxLength(100); }); }
We should make sure not to forget to call the base method as it will configure the rest of the identity framework’s database. Then we can add our configuration as usual.
We configure DisplayName
to be required and be at most 100 characters long. We could configure constraints on properties of the IdentityUser
class too, for example, introduce length constraints for UserName
or change column names.
Custom Navigation Properties on IdentityUser
Now, let’s introduce a more complex scenario, where we’d like to extend ApplicationUser
with related entities.
Let’s imagine that users can create posts on their page and each post can consist of a title and some text content.
Now, in order to bring this concept to life, let’s proceed by creating a Post
entity:
public class Post { public Guid Id { get; set; } public string Title { get; set; } public string Text { get; set; } }
To continue, let’s configure this entity in the OnModelCreating()
method:
builder.Entity<Post>(b => { b.HasKey(e => e.Id); b.Property(e => e.Title).IsRequired().HasMaxLength(256); b.Property(e => e.Text).IsRequired(); });
We inform Entity Framework about the key property, mark everything required and set the max length of the Title
property to 256 characters.
Next, let’s add a Post
collection to our ApplicationUser
:
public class ApplicationUser : IdentityUser<Guid> { public string DisplayName { get; set; } public DateTime LastLoginDateTime { get; set; } public List<Post> Posts { get; set; } }
Once we have the Posts
property added, let’s configure the relationship in the OnModelCreating()
method:
builder.Entity<ApplicationUser>(b => { b.Property(u => u.DisplayName).IsRequired().HasMaxLength(100); b.HasMany(u => u.Posts).WithOne(); });
We set up the relationship to be a one-to-many without explicit foreign key properties.
Adding a Migration
Now that Entity Framework is configured, we can move forward with creating the migration.
To learn more about Entity Framework migrations, check out our previous article on the topic.
When using the default MVC project with scaffolded identity and SQL Server, dotnet will automatically create the first migration for us. However, since we changed the primary key’s type, we should remove that migration and create our own first migration. To do this using the dotnet CLI, let’s issue the command from the project’s directory:
dotnet ef migrations remove
We can see, that the 00000000000000_CreateIdentitySchema
migration has been removed, so now let’s add our new CreateIdentitySchema
migration:
dotnet ef migrations add CreateIdentitySchema
By examining the generated migration, we can confirm that the custom properties and foreign keys are generated correctly:
protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.CreateTable( name: "AspNetUsers", columns: table => new { Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false), DisplayName = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false), LastLoginDateTime = table.Column<DateTime>(type: "datetime2", nullable: false), }, constraints: table => { table.PrimaryKey("PK_AspNetUsers", x => x.Id); }); migrationBuilder.CreateTable( name: "Post", columns: table => new { Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false), Title = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false), Text = table.Column<string>(type: "nvarchar(max)", nullable: false), ApplicationUserId = table.Column<Guid>(type: "uniqueidentifier", nullable: true) }, constraints: table => { table.PrimaryKey("PK_Post", x => x.Id); table.ForeignKey( name: "FK_Post_AspNetUsers_ApplicationUserId", column: x => x.ApplicationUserId, principalTable: "AspNetUsers", principalColumn: "Id"); }); }
In this example, some parts of the migration are omitted to simplify the process of verifying the accurate generation of both primitive properties and the relationship with the related entity for the AspNetUsers
table. Now we just have to issue the final command to apply the migration to the database:
dotnet ef database update
When checking the table structure, we will see that we have successfully extended the AspNetUsers
table with our custom properties.
Conclusion
In this article, we’ve learned about extending IdentityUser with custom properties step by step. We created a descendant class and configured the identity framework to use this new class instead of IdentityUser
. Then we added our custom properties and configured Entity Framework to store them correctly.
We also looked at changing the type of the primary key for our custom ApplicationUser
, making the Guid
persistence unique at the database level.
All in all, we can say that Microsoft did a great job in terms of extensibility with the ASP.NET Identity Framework, it is a fairly simple task to extend or completely change classes used by the Identity Framework.