8

I'm using ASP.NET core identity with EF end I would like to store data related to the user in the authentication cookie.

This is how I used to do with ASP.NET 4.6 (appcontext is the data to store):

public static void IdentitySignin(AppContext appContext, string providerKey = null, bool isPersistent = false)
{
    var claims = new List<Claim>();

    // create *required* claims
    claims.Add(new Claim(ClaimTypes.NameIdentifier, appContext.UserId.ToString()));
    claims.Add(new Claim(ClaimTypes.Name, appContext.UserName));

    // serialized AppUserState object
    claims.Add(new Claim("appcontext" + EZA.Store.AppContext.Version, appContext.ToString()));

    var identity = new ClaimsIdentity(claims, DefaultAuthenticationTypes.ApplicationCookie);

    // add to user here!
    AuthenticationManager.SignIn(new AuthenticationProperties()
    {
        AllowRefresh = true,
        IsPersistent = isPersistent,
        ExpiresUtc = DateTime.UtcNow.AddDays(7),
    }, identity);
}

but now I'm using ASP.NET Identity with EF and I can't find a way to store some data in the cookie.

Rey
  • 3,663
  • 3
  • 32
  • 55
EricImhauser
  • 701
  • 2
  • 7
  • 17

2 Answers2

8

Use AddClaimsAsync or AddClaimAsync of UserManager<YourUserIdentity>. for exemple like this when you sign in your user:

public class AccountController : Controller
{
    public UserManager<YourUserIdentity> UserManager { get; private set; }

    public SignInManager<YourUserIdentity> SignInManager { get; private set; }

    public AccountController(UserManager<YourUserIdentity> userManager, 
        SignInManager<YourUserIdentity> signInManager)
    {
        UserManager = userManager;
        SignInManager = signInManager;
    }

    public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
    {
        if (ModelState.IsValid)
        {
            var user = await UserManager.FindByNameAsync(model.UserName);

            await UserManager.AddClaimAsync(user, new Claim("your-claim", "your-value"));

            var signInStatus = await SignInManager.PasswordSignInAsync(user, model.Password, model.RememberMe, lockoutOnFailure: false);

            if (signInStatus.Succeeded)
                return RedirectToLocal(returnUrl);

            ModelState.AddModelError("", "Invalid username or password.");
            return View(model);
        }

        // If we got this far, something failed, redisplay form
        return View("Index", new LoginPageViewModel() { Login = model });
    }
 }
agua from mars
  • 16,428
  • 4
  • 61
  • 70
7

Before i read @aqua's answer(i have learned this way just now), i would say that you have two options:

1 - Overriding UserClaimsPrincipalFactory like below:

public class AppClaimsPrincipalFactory : UserClaimsPrincipalFactory<ApplicationUser, IdentityRole>
{
    public AppClaimsPrincipalFactory(
        UserManager<ApplicationUser> userManager,
        RoleManager<IdentityRole> roleManager,
        IOptions<IdentityOptions> optionsAccessor) : base(userManager, roleManager, optionsAccessor)
    {
    }

    public async override Task<ClaimsPrincipal> CreateAsync(ApplicationUser user)
    {
        var principal = await base.CreateAsync(user);

        ((ClaimsIdentity)principal.Identity).AddClaims(new[] {
             new Claim("<claim name>", value)
        });

        return principal;
    }
}

// register it
services.AddScoped<IUserClaimsPrincipalFactory<ApplicationUser>, AppClaimsPrincipalFactory>();

2- Using OnSigningIn event.

        services.Configure<IdentityOptions>(opt =>
        {
            opt.Cookies.ApplicationCookie.Events = new CookieAuthenticationEvents()
            {
                OnSigningIn = async (context) =>
                {
                    ClaimsIdentity identity = (ClaimsIdentity)context.Principal.Identity;
                    identity.AddClaim(new Claim("<claim name>", value));
                }
            };
        });
adem caglin
  • 22,700
  • 10
  • 58
  • 78
  • I did not know these options either – agua from mars Sep 18 '16 at 14:02
  • Option 2 looks interesting. Does it also store claim in database? – liang Jul 09 '19 at 07:16
  • Been searching for hours for a way to persist a custom claim from http call to http call, tried a dozen things and none of them actually do it. I tried this, but in .NET Core 3.1, IdentityOptions has no Cookies property. – EGP Aug 14 '20 at 04:24
  • Option 2 worked for me (with .Net 6). OnSigningIn fires before the authentication cookie is created, so that claims added there are persisted. I was able to pass parameters from the Login page to the event hander via HttpContext.Items. OnSigningIn can also test for other "session" claims (not saved to the Identity store) that have previously been added. – mcb2k3 Oct 25 '22 at 06:28