Is there a way to store some custom datas from the user inside the cookie generated by the Identity API ?
We are building an multi-tenant application, and so multiple companies can access to the same instance of our application. That's why I need, for a specific user, to store company code from the user in the identity cookie to retrieve the datas from the user when he returned on the web application after closing the browser.
you could achieve that by implementing a custom UserClaimsPrincipalFactory and adding a custom claim for your store number, then it would be stored in the cookie with the other claims.
Below is example code from my project where I am adding several custom claims including a SiteGuid because my scenario is also multi-tenant
using cloudscribe.Core.Models;
using Microsoft.AspNet.Identity;
using Microsoft.Extensions.OptionsModel;
using System;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
namespace cloudscribe.Core.Identity
{
public class SiteUserClaimsPrincipalFactory<TUser, TRole> : UserClaimsPrincipalFactory<TUser, TRole>
where TUser : SiteUser
where TRole : SiteRole
{
public SiteUserClaimsPrincipalFactory(
ISiteRepository siteRepository,
SiteUserManager<TUser> userManager,
SiteRoleManager<TRole> roleManager,
IOptions<IdentityOptions> optionsAccessor) : base(userManager, roleManager, optionsAccessor)
{
if (siteRepository == null) { throw new ArgumentNullException(nameof(siteRepository)); }
siteRepo = siteRepository;
options = optionsAccessor.Value;
}
private ISiteRepository siteRepo;
private IdentityOptions options;
public override async Task<ClaimsPrincipal> CreateAsync(TUser user)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
var userId = await UserManager.GetUserIdAsync(user);
var userName = await UserManager.GetUserNameAsync(user);
var id = new ClaimsIdentity(
options.Cookies.ApplicationCookie.AuthenticationScheme,
Options.ClaimsIdentity.UserNameClaimType,
Options.ClaimsIdentity.RoleClaimType
);
id.AddClaim(new Claim(Options.ClaimsIdentity.UserIdClaimType, userId));
id.AddClaim(new Claim(Options.ClaimsIdentity.UserNameClaimType, userName));
if (UserManager.SupportsUserSecurityStamp)
{
id.AddClaim(new Claim(Options.ClaimsIdentity.SecurityStampClaimType,
await UserManager.GetSecurityStampAsync(user)));
}
if (UserManager.SupportsUserRole)
{
var roles = await UserManager.GetRolesAsync(user);
foreach (var roleName in roles)
{
id.AddClaim(new Claim(Options.ClaimsIdentity.RoleClaimType, roleName));
if (RoleManager.SupportsRoleClaims)
{
var role = await RoleManager.FindByNameAsync(roleName);
if (role != null)
{
id.AddClaims(await RoleManager.GetClaimsAsync(role));
}
}
}
}
if (UserManager.SupportsUserClaim)
{
id.AddClaims(await UserManager.GetClaimsAsync(user));
}
ClaimsPrincipal principal = new ClaimsPrincipal(id);
if (principal.Identity is ClaimsIdentity)
{
ClaimsIdentity identity = (ClaimsIdentity)principal.Identity;
Claim displayNameClaim = new Claim("DisplayName", user.DisplayName);
if (!identity.HasClaim(displayNameClaim.Type, displayNameClaim.Value))
{
identity.AddClaim(displayNameClaim);
}
Claim emailClaim = new Claim(ClaimTypes.Email, user.Email);
if (!identity.HasClaim(emailClaim.Type, emailClaim.Value))
{
identity.AddClaim(emailClaim);
}
ISiteSettings site = await siteRepo.Fetch(user.SiteId, CancellationToken.None);
if (site != null)
{
Claim siteGuidClaim = new Claim("SiteGuid", site.SiteGuid.ToString());
if (!identity.HasClaim(siteGuidClaim.Type, siteGuidClaim.Value))
{
identity.AddClaim(siteGuidClaim);
}
}
}
return principal;
}
}
}
Then in Startup you need to register your custom factory so it gets injected and used instead of the default one
services.AddScoped<IUserClaimsPrincipalFactory<SiteUser>, SiteUserClaimsPrincipalFactory<SiteUser, SiteRole>>();
another approach is to use Claims Transformation, however this approach does not store the extra claims in a cookie but instead updates the claims on each request, that is it adds more claims to the ones from the cookie for the lifetime of the request but doesn't modify the claims in the cookie.
public class ClaimsTransformer : IClaimsTransformer
{
public Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
{
((ClaimsIdentity)principal.Identity).AddClaim(new Claim("ProjectReader", "true"));
return Task.FromResult(principal);
}
}
then in startup.cs:
app.UseClaimsTransformation(new ClaimsTransformationOptions
{
Transformer = new ClaimsTransformer()
});