Search code examples
asp.netasp.net-identityasp.net-identity-3

Are there any implementations of ASP.NET Identitity that have another level above account?


I am using ASP.NET Identity. It works well but I would like to add in a parent to the AspNetUsers table. In my case I would like to have each user belong to an organization. At this point I am just looking for some ideas to see if others have seen implementations that would allow this.

Has anyone seen any implementations that do this. I would like to get some tips on how I could implement this functionality.


Solution

  • I'm presuming you are using default EF implementation of Identity storage.

    Identity is very flexible and can be bent into many shapes to suit your needs.

    If you are looking for a simple parent-child relationship, where every user would have a parent record (such as Company), one of the ways to implement that is to add company reference to user class:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel.DataAnnotations.Schema;
    using Microsoft.AspNet.Identity.EntityFramework;
    
    
    public class ApplicationUser : IdentityUser
    {
        public ApplicationUser()
        {
        }
    
        [ForeignKey("CompanyId")]
        public Company Company { get; set; }
        public int CompanyId { get; set; }
    }
    
    
    public class Company
    {
        [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int CompanyId { get; set; }
        public String Name { get; set; }
    
        public virtual ICollection<ApplicationUser> Users { get; set; }
    }
    

    This will put a foreign key on users to companies. But from here next course of action depends on what is required in your application. I would imagine that you'll have some sort of restriction for users depending on what company they belong to. For quick company retrieval you can store CompanyId in a claim when logging in the user.
    Default implementation of ApplicationUser has GenerateUserIdentityAsync method. You can modify this as following:

        public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
        {
            // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
            var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
            // Add custom user claims here
            identity.AddClaim(new Claim("CompanyId", CompanyId.ToString()));
            return userIdentity;
        }
    

    Then on every request you'll be able to access this CompanyId claim from the cookie:

        public static int GetCompanyId(this IPrincipal principal)
        {
            var claimsPrincipal = principal as ClaimsPrincipal;
            //TODO check if claims principal is not null
    
            var companyIdString = claimsPrincipal.Claims.FirstOrDefault(c => c.Type == "CompanyId");
            //TODO check if the string is not null
    
            var companyId = int.Parse(companyIdString); //TODO this possibly can explode. Do some validation
            return companyId;
        }
    

    And then you'll be able to call this extension method from almost anywhere of your web application: HttpContext.Current.User.GetCompanyId()