Identity 2.0: Creating custom ClaimsIdentity eg: User.Identity.GetUserById<int>(int id) for Per Request Validation

11,646

Solution 1

The IIdentity object in MVC is going to be the issued token that corresponds to the identity of the user. This differs from whatever object or method you use on the back-end that represents the user (say a User class). If you want to use the user's identity to get a custom value then you need to put it into their claims object (ie the identity token) when they sign in (or at some other point in time).

You can add a claim at any time by giving the user an identity.

AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
identity.AddClaim(new Claim("PhoneNumber", "123-456-7890"));
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);

When you have that claim inserted into their token you can retrieve it using an extension method like this...

public static string GetPhoneNumber(this IIdentity identity)
{
    return ((ClaimsIdentity)identity).FindFirstValue("PhoneNumber");
}

Razor

@using MyProject.Web.Extensions

<img src="@User.Identity.GetPhoneNumber()" />

Solution 2

I actually found the solution using the answer to this SO question by LukeP but as Shoe notes, this is pre-MVC5 and we could simply put in a Claim instead.

I made the following alterations:

    interface IDomainPrincipal : IPrincipal
    {
        int Id { get; set; }
        string UserName { get; set; }
        string AvatarUrl { get; set; }
    }

    public class DomainPrincipal : IDomainPrincipal
    {
        public IIdentity Identity { get; private set; }
        public bool IsInRole(string role) { return false; }

        public DomainPrincipal(string email)
        {
            this.Identity = new GenericIdentity(email);
        }

        public int Id { get; set; }
        public string UserName { get; set; }
        public string AvatarUrl { get; set; }
    }

Then I used @User.Id, @User.UserName, @User.AvatarUrl in my @Razor Views respectively

Share:
11,646
yardpenalty.com
Author by

yardpenalty.com

I used to get paid to be a an RPG/PHP Web Developer, but I had set a goal of becoming an ASP.NET MVC Architect when I was a full-time college student and thought I was going to one day for sure be a Microsoft MVP. Now I am most experienced in Linux environments where I let PHP and Javascript do most of the heavy lifting. I can knock out a responsive layout for an entire Web App in a matter of weeks. There is one thing... I took a break so I could renovate my house and find out the true meaning of life. Well it worked! I no longer care about what my title is or where I am in the rankings of societal norms... I just want to live life the way it was intended to be lived. I still love programming and have a huge project I am working on and hope to launch it next Fall. I can't talk too much about it because I haven't enough lawyers to protect my idea from the vultures. I tell you one thing though, there is going to be no greater sense of accomplishment when I get to roll out my OWN Web App that deals with something I have been passionate about since a child. Not too many people can say they did that. Then...After I sell my idea to Yahoo! Inc. I am going to invest all my money into a recycling company where all the proceeds are used to help take care of all the sick and feral Kitties that struggle through the winter months here in West Michigan. God Bless! Hard work will always profit while laziness results in forced labor.

Updated on July 08, 2022

Comments

  • yardpenalty.com
    yardpenalty.com almost 2 years

    See this similar question: Need access more user properties in User.Identity

    I would like to create custom authentication methods to use with my Razor Views that allows easy access IdentityUser properties relational to the User.Identity object but I am not sure how to go about it. I want to create several custom extensions similar to User.Identity.GetUserName(), User.Identity.GetUserById(), etc... instead of using this ViewContextExtension method. My Authentication type is currently the default type DefaultAuthenticationTypes.ApplicationCookie from VS2013 MVC5 template. As Shoe stated, I need this claim to be inserted after the user signs in.

    My questions is:

    How and where do you create a custom claim that has an out parameter of this IIdentity under IPrincipal?

    This would allow me to access User properties via CookieAuthentication in a View for entities in a DDD setting where I have multiple DbContexts in a single app using Identity 2.0. I will eventually use WebAPI, but for now I want to keep it as simple as possible. I have found this SO Q&A but it is geared towards Web Forms using Tickets. Not sure the difference between tickets and tokens either?

    This is the current approach that uses ViewContext from a base controller:

    View:

        @using Microsoft.AspNet.Identity
        @using Globals.Helpers
        @using Identity //custom Identity for Domain
        @using Microsoft.AspNet.Identity.Owin
        
        @if (Request.IsAuthenticated)
        {
              var url = @ViewContext.BaseController().GetAvatarUrlById(User.Identity.GetUserId<int>());
      
            //...
        }
    

    BaseController.cs

            public string GetAvatarUrlById(int id)
            {
    
                var user = UserManager.FindById(id);
    
                return "../../" + user.ImageUrl;
            }
    

    Extensions.cs

        public static class ViewContextExtension
        {
            public static BaseController BaseController(this ViewContext view)
            {
                var baseController = (BaseController)view.Controller;
                return baseController;
            }
        }
    

    What I am looking for is but where & how?

    View:

    <img src="@User.Identity.GetAvatarUrl()" alt="User.Identity.GetAvatarUrl()" />
    

    SOLUTION

    I simply edited the Extension.cs file and used inheritance for the Base controller which is used for the _LoginPartial.cshtml & edited the ViewContextExtension class:

        #region ViewContextExt
        public static class ViewContextExtension
        {
            public static BaseController BaseController(this ViewContext view)
            {
                var baseController = (BaseController)view.Controller;
                return baseController;
            }
    
            public static string GetAvatarUrl(this IIdentity identity)
            {
                return ((ClaimsIdentity)identity).Claims.First(c => c.Type == "AvatarUrl").Value;
            }
        }
    }
    # endregion
    
  • yardpenalty.com
    yardpenalty.com over 9 years
    Once I get solution complete with example I will mark answer :-)
  • yardpenalty.com
    yardpenalty.com over 9 years
    Thanks Shoe I was actually reading on PerRequest Validation prior to your answer so I was almost there. SO what you are saying is if I want to redirect anonymous users who are attempting to view pages which its ActionResult returns a model that use parameter == User.Identity.GetUserId a simple if(User.Identity.IsAuthenticated) will not suffice? I need to validate that the models Id is equal to to the User.Identity.GetUserId? I understand that it would make most sense not to pass any id when it comes to preventing anonymous users but for arguments sake. Thanks again!
  • jamesSampica
    jamesSampica over 9 years
    If you want to redirect anonymous users you should do it at the controller level, though a properly placed Authorize attribute is generally better unless it's a page that can be viewed by both anonymous and authenticated users. You don't have to check the model's id against the identity because they should be the same. The point I was making was that the Identity of the user (User.Identity) is a different object with different properties than your User class, and you can't simply grab the User properties without first putting them into the Identity object (via claims).
  • yardpenalty.com
    yardpenalty.com over 9 years
    Ok so the [Authorize] attribute is in fact validating the claim/token not just the user is in a role (unless specified). Was curious because the default template in VS2013 for MVC 5 doesn't implement much Authentication. Now looking at the ManageController again it does use [Authorize] attribute. It looks like I have a lot of reading to do on Identity 2.0 in order to create the necessary boilerplate code for both Web API and MVC usage. Thanks again for all your help.
  • jamesSampica
    jamesSampica over 9 years
    Yes that's correct. The template gives a small bootstrapped project but isn't really meant as a tutorial.
  • yardpenalty.com
    yardpenalty.com over 9 years
    To add to your answer Shoe, instead of using claims we can override the HttpContext.User by implementing IPrincipal instead of IIdentity I think? Either way we are creating these objects within the controller which totally makes sense now. Will have to look into the difference between claims and LukeP's approach using IPrincipal.
  • jamesSampica
    jamesSampica over 9 years
    The answer you link to is pre-MVC5. You're making it harder for yourself. MVC 5 has claims already. Extending IPrincipal to accomplish the same goal isn't necessary.
  • yardpenalty.com
    yardpenalty.com over 9 years
    Alright thanks Shoe. I get it now. I will offer my solution using your approach. After all, the goal is to use MVC5.