Decoupling Asp.Net Identity 2.0 from Entity Framework in MVC5: Part 1


Usually the Asp.Net Identity 2.0 framework is aptly suited for most simple applications. However in complex line of business applications there is often a need to extend this base framework to map to growing application needs and architecture.
Today we are going to take a spin around the Asp.Net Identity 2.0 and see how we can de-couple it from the Entity framework and extend it further.

The implementation of the ApplicationDbContext that comes as a part of the standard template when we add Asp.Net Identity 2.0 to a MVC5 application is something like:

public class ApplicationUser : IdentityUser
{
    public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager) {
        var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
        return userIdentity;
    }
}

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public ApplicationDbContext()
        : base("DefaultConnection", throwIfV1Schema: false) {
    }

    static ApplicationDbContext() {
        Database.SetInitializer<ApplicationDbContext>(new ApplicationDbInitializer());
    }

    public static ApplicationDbContext Create() {
        return new ApplicationDbContext();
    }
}

The IdentityDbContext does inherit from DbContext and in additions takes care of the Asp.Net Identity entities and related entities.

We will cover the entire post in 3 parts

Part 1

  • Implement the IdentityUser, IdentityRole and IdentityUserRole classes to support role based configuration for Asp.Net Identity framework
  • Make the ApplicationDbContext work with DbContext instead of IdentityDbContext<TUser> class
Part 2
  • Implement the UserStore without Claims and External Logins support to work with the new IdentityUser class
  • Implement RoleStore to work with the new IdentityRole class

Part 3

  • Implement Custom UserManager class to work with our new UserStore and IdentityUser entity
  • Implement Custom RoleManager class to work with our new RoleStore and IdentiyRole entity
  • A sample MVC5 application working with our custom system implementing basic user authentication and registration process using Asp.Net Identity

IdentityUser and Configuration class

public class IdentityUser: IUser
{
    #region Constructors

    public IdentityUser()
    {
        this.Id = Guid.NewGuid().ToString();
        this.Roles = (ICollection<IdentityUserRole>)new List<IdentityUserRole>();
    }

    public IdentityUser(string username): this()
    {
        this.UserName = username;
    }

    #endregion

    #region IUser<string> Members

    public string Id { get; set; }

    [Index("UserNameIndex", IsUnique=true)]
    public virtual string UserName { get; set; }

    public virtual string PasswordHash { get; set; }

    public virtual string SecurityStamp { get; set; }

    public virtual string Email { get; set; }

    public virtual int AccessFailedCount { get; set; }

    public virtual bool EmailConfirmed { get; set; }

    public virtual ICollection<IdentityUserRole> Roles { get; set; }

    #endregion

    #region Methods

    public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<IdentityUser> manager)
    {
        var identity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
        return identity;
    }

    #endregion
}

public class IdentityUserConfiguration: EntityTypeConfiguration<IdentityUser>
{
    public IdentityUserConfiguration()
    {
        HasKey(t => t.Id);
        Property(t => t.Id).HasMaxLength(128);
        Property(t => t.UserName).IsRequired().HasMaxLength(256);
        Property(t => t.PasswordHash).IsRequired();
        Property(t => t.SecurityStamp).IsRequired();
        Property(t => t.Email).IsRequired().HasMaxLength(256);
        Property(t => t.EmailConfirmed).IsRequired();

        HasMany(t => t.Roles)
            .WithRequired(t => t.User)
            .HasForeignKey(t => t.UserId);

        ToTable("AspNetUsers");
    }
}

IdentityRole and configuration class

public class IdentityRole: IRole
{
    #region Constructors

    public IdentityRole()
    {
        this.Id = Guid.NewGuid().ToString();
    }

    public IdentityRole(string roleName): this()
    {
        this.Name = roleName;
    }

    public IdentityRole(string roleName, string id)
    {
        this.Name = roleName;
        this.Id = id;
    }

    #endregion

    #region IRole<string> Members

    public string Id { get; set; }

    [Index("RoleNameIndex", IsUnique=true)]
    public virtual string Name { get; set; }

    #endregion
}

public class IdentityRoleConfiguration: EntityTypeConfiguration<IdentityRole>
{
    public IdentityRoleConfiguration()
    {
        HasKey(t => t.Id);
        Property(t => t.Id).HasMaxLength(128);
        Property(t => t.Name).IsRequired().HasMaxLength(256);

        ToTable("AspNetRoles");
    }
}

IdentityUserRole and Configuration class

public class IdentityUserRole
{
    #region Properties

    public string UserId { get; set; }
    public virtual IdentityUser User { get; set; }

    public string RoleId { get; set; }
    public virtual IdentityRole Role { get; set; }

    #endregion

    #region Constructors

    public IdentityUserRole()
    {
    }

    public IdentityUserRole(string userId, string roleId)
    {
        this.UserId = userId;
        this.RoleId = roleId;
    }

    #endregion
}

public class IdentityUserRoleConfiguration: EntityTypeConfiguration<IdentityUserRole>
{
    public IdentityUserRoleConfiguration()
    {
        HasKey(t => new
        {
            t.RoleId,
            t.UserId
        });

        Property(t => t.UserId).HasColumnName("UserId");
        Property(t => t.RoleId).HasColumnName("RoleId");

        ToTable("AspNetUserRoles");
    }
}

And our new ApplicationDbContext class now becomes

public class ApplicationDbContext: DbContext
{
    static ApplicationDbContext()
    {
        Database.SetInitializer<ApplicationDbContext>(null);
    }

    public ApplicationDbContext()
        : base("DefaultConnection")
    {
        this.Configuration.ValidateOnSaveEnabled = true;
        this.Configuration.AutoDetectChangesEnabled = true;
    }

    public new IDbSet<T> Set<T>() where T : class
    {
        return base.Set<T>();
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Configurations.AddFromAssembly(typeof(IdentityModel.Configurations.IdentityUserConfiguration).Assembly);
    }
}

In the next part we will look at the implementations of:

  • UserStore
  • RoleStore

About Jinish Bhardwaj
Jinish works as a Software Architect for Tucows and has more than 14 years of experience in building high availability real time Web, Windows and Smart Client systems for clients across the globe. Jinish is also a Microsoft Certified Professional and a Certified SCRUM Master

3 Responses to Decoupling Asp.Net Identity 2.0 from Entity Framework in MVC5: Part 1

  1. Paul Divan says:

    This is just what I have been looking for. I have my own credential system that we use in our phone applications and I want to access it and make use of it through the ASP.net Identity interface. Is there anyway of getting the full project?

  2. Hi Paul, I have just been busy lately to do the follow up posts in the series. I am planning to finish the next two posts in the series in the coming week. In case you are looking for a quick reference source code for the article you can find it on my github here: https://github.com/JinishBhardwaj/CustomAspNetIdentity

    I havent had a chance to complete it to what i want it to look like, but it will give you a fair idea. Hope that helps.

  3. Hell Jinish, Is there any update on part 2 and part 3, thanks

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: