[asp.net-mvc] 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.

The answer is


For anyone that finds this question looking for how to access custom properties in ASP.NET Core 2.1 - it's much easier: You'll have a UserManager, e.g. in _LoginPartial.cshtml, and then you can simply do (assuming "ScreenName" is a property that you have added to your own AppUser which inherits from IdentityUser):

@using Microsoft.AspNetCore.Identity

@using <namespaceWhereYouHaveYourAppUser>

@inject SignInManager<AppUser> SignInManager
@inject UserManager<AppUser> UserManager

@if (SignInManager.IsSignedIn(User)) {
    <form asp-area="Identity" asp-page="/Account/Logout" asp-route-returnUrl="@Url.Action("Index", "Home", new { area = "" })" 
          method="post" id="logoutForm" 
          class="form-inline my-2 my-lg-0">

        <ul class="nav navbar-nav ml-auto">
            <li class="nav-item">
                <a class="nav-link" asp-area="Identity" asp-page="/Account/Manage/Index" title="Manage">
                    Hello @((await UserManager.GetUserAsync(User)).ScreenName)!
                    <!-- Original code, shows Email-Address: @UserManager.GetUserName(User)! -->
                </a>
            </li>
            <li class="nav-item">
                <button type="submit" class="btn btn-link nav-item navbar-link nav-link">Logout</button>
            </li>
        </ul>

    </form>
} else {
    <ul class="navbar-nav ml-auto">
        <li class="nav-item"><a class="nav-link" asp-area="Identity" asp-page="/Account/Register">Register</a></li>
        <li class="nav-item"><a class="nav-link" asp-area="Identity" asp-page="/Account/Login">Login</a></li>
    </ul>
}

Check out this great blog post by John Atten: ASP.NET Identity 2.0: Customizing Users and Roles

It has great step-by-step info on the whole process. Go read it : )

Here are some of the basics.

Extend the default ApplicationUser class by adding new properties (i.e.- Address, City, State, etc.):

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

    // Use a sensible display name for views:
    [Display(Name = "Postal Code")]
    public string PostalCode { get; set; }

    // Concatenate the address info for display in tables and such:
    public string DisplayAddress
    {
        get
        {
            string dspAddress = string.IsNullOrWhiteSpace(this.Address) ? "" : this.Address;
            string dspCity = string.IsNullOrWhiteSpace(this.City) ? "" : this.City;
            string dspState = string.IsNullOrWhiteSpace(this.State) ? "" : this.State;
            string dspPostalCode = string.IsNullOrWhiteSpace(this.PostalCode) ? "" : this.PostalCode;

            return string.Format("{0} {1} {2} {3}", dspAddress, dspCity, dspState, dspPostalCode);
        }
    }

Then you add your new properties to your RegisterViewModel.

    // Add the new address properties:
    public string Address { get; set; }
    public string City { get; set; }
    public string State { get; set; }

Then update the Register View to include the new properties.

    <div class="form-group">
        @Html.LabelFor(m => m.Address, new { @class = "col-md-2 control-label" })
        <div class="col-md-10">
            @Html.TextBoxFor(m => m.Address, new { @class = "form-control" })
        </div>
    </div>

Then update the Register() method on AccountController with the new properties.

    // Add the Address properties:
    user.Address = model.Address;
    user.City = model.City;
    user.State = model.State;
    user.PostalCode = model.PostalCode;

I also had added on or extended additional columns into my AspNetUsers table. When I wanted to simply view this data I found many examples like the code above with "Extensions" etc... This really amazed me that you had to write all those lines of code just to get a couple values from the current users.

It turns out that you can query the AspNetUsers table like any other table:

 ApplicationDbContext db = new ApplicationDbContext();
 var user = db.Users.Where(x => x.UserName == User.Identity.Name).FirstOrDefault();

I was looking for the same solution and Pawel gave me 99% of the answer. The only thing that was missing that I needed for the Extension to display was adding the following Razor Code into the cshtml(view) page:

@using programname.Models.Extensions

I was looking for the FirstName, to display in the top right of my NavBar after the user logged in.

I thought I would post this incase it helps someone else, So here is my code:

I created a new folder called Extensions(Under my Models Folder) and created the new class as Pawel specified above: IdentityExtensions.cs

using System.Security.Claims;
using System.Security.Principal;

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

IdentityModels.cs :

public class ApplicationUser : IdentityUser
{

    //Extended Properties
    public string FirstName { get; internal set; }
    public string Surname { get; internal set; }
    public bool isAuthorized { get; set; }
    public bool isActive { get; set; }

    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
        userIdentity.AddClaim(new Claim("FirstName", this.FirstName));

        return userIdentity;
    }
}

Then in my _LoginPartial.cshtml(Under Views/Shared Folders) I added @using.ProgramName.Models.Extensions

I then added the change to the folling line of code that was going to use the Users First name after Logging in :

@Html.ActionLink("Hello " + User.Identity.GetUserFirstname() + "!", "Index", "Manage", routeValues: null, htmlAttributes: new { title = "Manage" })

Perhaps this helps someone else down the line.


Dhaust gives a good way to add the property to the ApplicationUser class. Looking at the OP code it appears they may have done this or were on track to do that. The question asks

How can I retrieve the OrganizationId property of the currently logged in user from within a controller? However, OrganizationId is not a property available in User.Identity. Do I need to extend User.Identity to include the OrganizationId property?

Pawel gives a way to add an extension method that requires using statements or adding the namespace to the web.config file.

However, the question asks if you "need to" extend User.Identity to include the new property. There is an alternative way to access the property without extending User.Identity. If you followed Dhaust method you can then use the following code in your controller to access the new property.

ApplicationDbContext db = new ApplicationDbContext();
var manager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(db));
var currentUser = manager.FindById(User.Identity.GetUserId());
var myNewProperty = currentUser.OrganizationId;

Examples related to asp.net-mvc

Using Lato fonts in my css (@font-face) Better solution without exluding fields from Binding Vue.js get selected option on @change You must add a reference to assembly 'netstandard, Version=2.0.0.0 How to send json data in POST request using C# VS 2017 Metadata file '.dll could not be found The default XML namespace of the project must be the MSBuild XML namespace How to create roles in ASP.NET Core and assign them to users? The model item passed into the dictionary is of type .. but this dictionary requires a model item of type How to use npm with ASP.NET Core

Examples related to asp.net-mvc-5

'router-outlet' is not a known element The type or namespace name 'System' could not be found How to get 'System.Web.Http, Version=5.2.3.0? EntityType 'IdentityUserLogin' has no key defined. Define the key for this EntityType How to extend available properties of User.Identity How to implement oauth2 server in ASP.NET MVC 5 and WEB API 2 How to get JSON object from Razor Model object in javascript The model backing the 'ApplicationDbContext' context has changed since the database was created How can I implement a theme from bootswatch or wrapbootstrap in an MVC 5 project? MVC 5 Access Claims Identity User Data

Examples related to asp.net-identity

ASP.NET Core Identity - get current user How to get current user in asp.net core Adding ASP.NET MVC5 Identity Authentication to an existing project How to get the current logged in user Id in ASP.NET Core How to extend available properties of User.Identity Get current user id in ASP.NET Identity 2.0 Get the current user, within an ApiController action, without passing the userID as a parameter ASP.NET Identity - HttpContext has no extension method for GetOwinContext ASP.NET MVC 5 - Identity. How to get current ApplicationUser OWIN Security - How to Implement OAuth2 Refresh Tokens

Examples related to asp.net-identity-2

How to extend available properties of User.Identity