Search code examples
asp.net-mvcasp.net-identity

Proper place for adding custom user claims


There is a user class, which has field named "Avatar", which stores path to his profile picture. I want to show it in header inside the partial view. So I desided to add a claim to user identity. I put this lines of code inside my IdentityConfig.cs class:

 public override Task<ClaimsIdentity> CreateUserIdentityAsync(AppUser user)
        {
            if(!System.String.IsNullOrEmpty(user.Avatar))
                user.Claims.Add(new AppUserClaim() { ClaimType = "avatar", ClaimValue = user.Avatar});

            return user.GenerateUserIdentityAsync((AppUserManager)UserManager);
        }

But there is a problem: after a period of time (aprox. 1 hour) this Claim disappears and there's no avatar shown. I found out, that asp.net identity framework regenerates user's identity every 30 minutes (default). And according to this:

 regenerateIdentityCallback: (manager, user) =>
                            user.GenerateUserIdentityAsync(manager)

it calls GenerateUserIdentityAsyncmethod of user's class. At this momment it becomes not clear to me. There are two, on the first sight, similar methods of generating user identity:

  1. Inside AppUser class, which takes usermanager class as a parameter - public async Task<ClaimsIdentity> GenerateUserIdentityAsync(AppUserManager manager)
  2. Inside SignInManager- public override Task<ClaimsIdentity> CreateUserIdentityAsync(AppUser user)

What's the purpose of that? Where each of this methods has been used? And which one should I use to add custom user claim?


Solution

  • I've refactored a standard ASP.NET MVC project a little so I don't repeat the code for adding claims.

    Startup.Auth.cs:

    public void ConfigureAuth(IAppBuilder app, Container container)
    {
        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            Provider = new CookieAuthenticationProvider
            {
                OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, User>(
                    validateInterval: TimeSpan.FromMinutes(30),
                    regenerateIdentity: (manager, user) => IdentityHelper.GenerateUserIdentityAsync(user, manager))
            }
        });
    }
    

    Then I made a static helper method to generate the identity:

    public async Task<ClaimsIdentity> GenerateUserIdentityAsync(User user, UserManager<User> manager)
    {
        var userIdentity = await manager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie).ConfigureAwait(false);
    
        userIdentity.AddClaim(new Claim("Key", "Value"));
    
        return userIdentity;
    }
    

    Now you will be able to reuse this helper from your SignInManager.

    public class ApplicationSignInManager : SignInManager<User, string>
    {
        public ApplicationSignInManager(ApplicationUserManager userManager, IAuthenticationManager authenticationManager)
            : base(userManager, authenticationManager)
        {
        }
    
        public override Task<ClaimsIdentity> CreateUserIdentityAsync(User user)
        {
            return IdentityHelper.GenerateUserIdentityHelperAsync(user, (ApplicationUserManager)UserManager);
        }
    }