ASP.Net

ASP.NET Identity 2.0: Extensible Template Projects


Image by Tom Magliery  | Some Rights Reserved

We’ve spent the last few posts looking at various customizations of the basic Identity Samples project. I decided to take what we’ve learned so far and create a general, easily extensible template project, so that we don’t have to futz about nearly from scratch every time we want some customization of our Identity models.

We’ve been working with the Identity Samples project as a starting point, playing with and building upon the work of the Identity team in order to explore the Identity 2.0 framework. With the Identity Samples project, the Identity team has provided a reasonable jumping off point, but it is far from a one-size fits all solution.

In ASP.NET Identity 2.0: Customizing Users and Roles we looked at some basic customization of the IdentityUser and IdentityRole models. With ASP.NET Identity 2.0 Extending Identity Models and Using Integer Keys Instead of Strings we went several steps further, and basically needed to re-implement all of the basic Identity model classes from their generic base classes, in order to use integer keys instead of the default string-based keys in the original Identity Samples project.

I’ve applied the concepts we covered in those two posts to build out a pair of template projects from which extending any of the Identity model classes is straightforward.

ASP.NET Identity 2.0 Extensible Project Templates on Github

I’ve created two repos on Github, one for a template project with string keys (adhering to the Identity team’s decision to use string-based keys as the default), and another which uses integer keys.

The choice of key type has significantly far-reaching impact upon the project code base that it makes more sense to use a separate template for each.

To get a better feel for the how and why this was necessary, see the previous posts:

Extending the Basic Classes

So long as you are comfortable with your choice of Key type, you can now easily extend any of the core Identity model classes. If we take a look at the Models => IdentityModels.cs file we find the basic classes we need. Since they are all now defined in terms of each other with respect to the generic type arguments, we can easily add al properties or methods to any of the classes found here without throwing the compiler into a tizzy.

A quick look at the code for the string-based key version of the project shows us where we can add our own properties or methods and extend the basic classes:

The Basic Identity Models, Extended:
// You will not likely need to customize there, but it is necessary/easier to create our own 
// project-specific implementations, so here they are:
public class ApplicationUserLogin : IdentityUserLogin<string> { }
public class ApplicationUserClaim : IdentityUserClaim<string> { }
public class ApplicationUserRole : IdentityUserRole<string> { }
  
// Must be expressed in terms of our custom Role and other types:
public class ApplicationUser 
    : IdentityUser<string, ApplicationUserLogin, 
    ApplicationUserRole, ApplicationUserClaim>
{
    public ApplicationUser()
    {
        this.Id = Guid.NewGuid().ToString();
        // Add any custom User properties/code here
    }
  
  
    public async Task<ClaimsIdentity>
        GenerateUserIdentityAsync(ApplicationUserManager manager)
    {
        var userIdentity = await manager
            .CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
        return userIdentity;
    }
}
  
  
// Must be expressed in terms of our custom UserRole:
public class ApplicationRole : IdentityRole<string, ApplicationUserRole>
{
    public ApplicationRole() 
    {
        this.Id = Guid.NewGuid().ToString();
    }
  
    public ApplicationRole(string name)
        : this()
    {
        this.Name = name;
    }
  
    // Add any custom Role properties/code here
}
  
  
// Must be expressed in terms of our custom types:
public class ApplicationDbContext 
    : IdentityDbContext<ApplicationUser, ApplicationRole, 
    string, ApplicationUserLogin, ApplicationUserRole, ApplicationUserClaim>
{
    public ApplicationDbContext()
        : base("DefaultConnection")
    {
    }
  
    static ApplicationDbContext()
    {
        Database.SetInitializer<ApplicationDbContext>(new ApplicationDbInitializer());
    }
  
    public static ApplicationDbContext Create()
    {
        return new ApplicationDbContext();
    }
  
    // Add additional items here as needed
}
// Most likely won't need to customize these either, but they were needed because we implemented
// custom versions of all the other types:
public class ApplicationUserStore 
    :UserStore<ApplicationUser, ApplicationRole, string,
        ApplicationUserLogin, ApplicationUserRole, 
        ApplicationUserClaim>, IUserStore<ApplicationUser, string>, 
    IDisposable
{
    public ApplicationUserStore()
        : this(new IdentityDbContext())
    {
        base.DisposeContext = true;
    }
  
    public ApplicationUserStore(DbContext context)
        : base(context)
    {
    }
}
  
  
public class ApplicationRoleStore
: RoleStore<ApplicationRole, string, ApplicationUserRole>,
IQueryableRoleStore<ApplicationRole, string>,
IRoleStore<ApplicationRole, string>, IDisposable
{
    public ApplicationRoleStore()
        : base(new IdentityDbContext())
    {
        base.DisposeContext = true;
    }
  
    public ApplicationRoleStore(DbContext context)
        : base(context)
    {
    }
}

Since we’ve already covered most of what went into this, and why, we won’t here.

Feel free to clone/fork away, and if you find any bugs, feel free to open an issue, shoot me a pull request, or both!

Additional Resources and Items of Interest

ASP.Net
Send Email to Selected Recipients from your ASP.NET MVC Web Application Part II
C#
C#: Using Reflection and Custom Attributes to Map Object Properties
ASP.Net
Reverse-Engineering an Existing Database in your ASP.NET MVC Application Using Entity Framework Model/Database-First
  • sage

    sagesage

    Author Reply

    Nice and helpful article, John. In a situation where I have different type of users that needs authentication, lets say Student, Teacher, Parent and Administrator, should each of these users extend the Application user class?


    • John Atten

      John AttenJohn Atten

      Author Reply

      Hmmm…I think not, although without knowing more about your use case, it’s up for debate. Just remember, inheritance should be almost the solution of last resort. The best starting point for your case would be to create roles representing your various user types. You may well end up with some classes in your application, all deriving from a common entity (‘Person’?), but this has nothing to do with the authentication, and should be kept separate.


  • John Atten

    John AttenJohn Atten

    Author Reply

    GREAT! Thanks for reading, and taking the time to comment!


  • Riaan

    RiaanRiaan

    Author Reply

    A real lifesaver.
    I've been following your ASP.NET Identity and Identity 2.0 posts for a while now in my journey to learning ASP.NET MVC 5 and it is of great help to me.