Search code examples
asp.net-mvcasp.net-mvc-5asp.net-identityasp.net-identity-2

How to extend available properties of User.Identity


I'm using MVC5 Identity 2.0 for users to log into my website, where the authentication details are stored in an SQL database. Asp.net Identity has been implemented in a standard way as can be found in many online tutorials.

The ApplicationUser class in IdentityModels has been extended to include some custom properties, such as an integer OrganizationId. The idea is that many users can be created and assigned to a common Organization for database relationship purposes.

public class ApplicationUser : IdentityUser
    {
        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
            return userIdentity;
        }

        //Extended Properties
        public DateTime? BirthDate { get; set; }
        public long? OrganizationId { get; set; }

        //Key Mappings
        [ForeignKey("OrganizationId")]
        public virtual Organization Organization { get; set; }
    }

How can I retrieve the OrganizationId property of the currently logged in user from within a controller? Is this available via a method once a user is logged in or do I always have the retrieve the OrganizationId from the database, based on the UserId, every time a controller method executes?

Reading around on the web I have seen I need to use the following to get the logged in UserId etc.

using Microsoft.AspNet.Identity;
...
User.Identity.GetUserId();

However, OrganizationId is not a property available in User.Identity. Do I need to extend User.Identity to include the OrganizationId property? If so, how do I go about this.

The reason I need the OrganizationId so often is that many table queries are reliant on the OrganizationId to retrieve data relevant to the Organization that's associated to the logged in user.


Solution

  • Whenever you want to extend the properties of User.Identity with any additional properties like the question above, add these properties to the ApplicationUser class first like so:

    public class ApplicationUser : IdentityUser
    {
        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
            return userIdentity;
        }
    
        // Your Extended Properties
        public long? OrganizationId { get; set; }
    }
    

    Then what you need is to create an extension method like so (I create mine in an new Extensions folder):

    namespace App.Extensions
    {
        public static class IdentityExtensions
        {
            public static string GetOrganizationId(this IIdentity identity)
            {
                var claim = ((ClaimsIdentity)identity).FindFirst("OrganizationId");
                // Test for null to avoid issues during local testing
                return (claim != null) ? claim.Value : string.Empty;
            }
        }
    }
    

    When you create the Identity in the ApplicationUser class, just add the Claim -> OrganizationId like so:

        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 => this.OrganizationId is a value stored in database against the user
            userIdentity.AddClaim(new Claim("OrganizationId", this.OrganizationId.ToString()));
    
            return userIdentity;
        }
    

    Once you added the claim and have your extension method in place, to make it available as a property on your User.Identity, add a using statement on the page/file you want to access it:

    in my case: using App.Extensions; within a Controller and @using. App.Extensions withing a .cshtml View file.

    EDIT:

    What you can also do to avoid adding a using statement in every View is to go to the Views folder, and locate the Web.config file in there. Now look for the <namespaces> tag and add your extension namespace there like so:

    <add namespace="App.Extensions" />
    

    Save your file and you're done. Now every View will know of your extensions.

    You can access the Extension Method:

    var orgId = User.Identity.GetOrganizationId();