1

This is my first attempt to use the ASP.NET identity with the builtin authentication. All previous attempts resulted in having a manual check for user credentials and then setting FormsAuthentication AuthCookie. But I had some problems with a signalR connection not having authentication information this way. So I started from scratch with the builtin authentication.

So I extended my ApplicationUser object with some fields:

public class ApplicationUser : IdentityUser
{
    public long SteamId { get; set; }
    public string Avatar { get; internal set; }
    public string Name { get; internal set; }
    public int Credits { get; internal set; }
    public long? DiscordId { get; internal set; }
    public DateTime? LastLogin { get; internal set; }
    public DateTime RegisterDate { get; internal set; }
}

These fields will create new columns in my AspNetUsers table. The problem is, I can't access these values in my views. For that I need to use claims if I understand correctly. These claims are stored in another table called AspNetUserClaims. So I have to add those claims to the user

await UserManager.AddClaimAsync(user.Id, new Claim("Avatar", user.Avatar));

and creating a extension method to get the avatar from the principal

public static class ClaimsPrincipalExtension
{
    public static string GetAvatar(this ClaimsPrincipal principal)
    {
        var avatar = principal.Claims.FirstOrDefault(c => c.Type == "Avatar");
        return avatar?.Value;
    }
}

Now I can access the avatar in my view

@(((ClaimsPrincipal)User).GetAvatar())

I don't think that's a really good and clean way to do this, but this is the first time I am using it, so I don't know what's the best practices to do. There are three main reasons why I don't like it:

  1. The avatar is stored twice, once in the AspNetUsers table as column and once as a new entry in AspNetUserClaims
  2. Fields like SteamId, Credits or RegisterDate are saved as string in the AspNetUserClaims table, and I have to convert them to int, long or DateTime in the extension method
  3. I have to write an extension method for every property I'll add to the ApplicationUser

What is the best way to handle additional fields?

  • Might it be an option to create a new object called AdditionalUserInformation and store the json serialized string as claim and just have one extension method to return the object? Then the properties would have the correct type.
  • Or is there a way to access the properties of the ApplicationUser in the view?
  • Or is the problem the use of those in the view? Should I access them in the controller and create a Model for the view containing all information? What about a _Layout page or a _navigation partial view then? They also might need the information of the user. I think I can't feed them with the controllers.

I also checked some examples. Mostly they add those extension methods and mostly just string properties.

Examples

I am currently kinda stuck here on finding the best and maybe clean solution.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
mrdiablo
  • 53
  • 1
  • 6
  • Identity 1 or Identity 2? – Erik Philips Nov 28 '18 at 17:57
  • Sorry, using Identity 2. – mrdiablo Nov 28 '18 at 18:10
  • 1
    `Should I access them in the controller and create a Model for the view containing all information?` this is generally the route i always go as I prefer to keep as much c# out of my views as possible and have views essentially only be markup and accessing model properties. this isnt a big deal in your scenario and is entirely preference but it would also allow you to not have to worry about writing claims extensions as youre finding out now – GregH Nov 28 '18 at 19:44
  • What about a _Layout page or a _navigation partial view then? They also might need the information of the user. – mrdiablo Nov 28 '18 at 19:50

1 Answers1

0

You can access AspNetUsers table with the name "Users" from your DbContext. First query AspNetUsers with current user's username or userId, then fill your ViewModel and send it to the view. The following code shows what I described:

[Authorize]
public ActionResult UserTopNavBar()
{
     var userTopNavBarViewModel = new UserTopNavBarViewModel();
     using(ApplicationDbContext _db = new ApplicationDbContext())
     {
         var user = _db.Users.FirstOrDefault(a => a.UserName == User.Identity.Name);
         if (user != null)
         {
             userTopNavBarViewModel.Name = user.FirstName + " " + user.LastName;
             userTopNavBarViewModel.Picture = user.Picture;
             userTopNavBarViewModel.Id = user.Id;
         }
     }
     return PartialView(userTopNavBarViewModel);
}

And this is my ViewModel

public class UserTopNavBarViewModel
{
     public string Name { get; set; }
     public byte[] Picture { get; set; }
     public string Id { get; set; }
}
vahid
  • 258
  • 2
  • 8
  • 1
    Thank you very much. Didn't know that it's possible to use a Controller for the partial views. With this in mind I added a PartialController with the attribute ChildActionOnly and use them in my layout view by using `@{ Html.RenderAction("SideNavigation", "Partial"); }` instead of RenderPartial. And So I moved everything into the controllers instead of having the logic in the views. I am accessing the user object in the controller instead of accessing the database directly. – mrdiablo Nov 30 '18 at 06:50
  • Thank you for your feedback. – vahid Nov 30 '18 at 09:27