Sunday, April 2, 2017

Storing Identity Claims in Session or Redis


ASP.NET Core Identity by default uses cookies to store claims. If your authorization is very granular then you will end up using many claims. With all these claims stored in cookie, the cookie size gets bigger and you can exceed the cookie limit.

We can achieve the authorization granularity without exceeding the cookie limit and impacting the way ASP.NET Identity authorizes users by storing the claims in session, Redis or any other memory storage.

Below are the steps I implemented to store the claims in session.

First let’s store the claims in session (assuming that the ASP.NET Core Session is already configured). During authentication, Identity system uses SignInManager for creating principal object of the logged in user. While creating the principal object, Identity populates the claims. We can override this SignInManager and store the claims as shown below:

public class ApplicationSignInManager : SignInManager<ApplicationUser>
{
    private IHttpContextAccessor contextAccessor;

    public ApplicationSignInManager(UserManager<ApplicationUser> userManager, IHttpContextAccessor contextAccessor, IUserClaimsPrincipalFactory<ApplicationUser> claimsFactory, IOptions<IdentityOptions> optionsAccessor, ILogger<SignInManager<ApplicationUser>> logger)
        : base(userManager, contextAccessor, claimsFactory, optionsAccessor, logger)
    {
        this.contextAccessor = contextAccessor;
    }

    public override async Task<ClaimsPrincipal> CreateUserPrincipalAsync(ApplicationUser user)
    {
        var principal = await base.CreateUserPrincipalAsync(user);
        ClaimsIdentity identity = (ClaimsIdentity)principal.Identity;

        // storing claims in session and removing them. These claims will be added by Transformer
        List<ClaimModel> sessionClaims = new List<ClaimModel>();
        List<Claim> identityClaims = identity.Claims.ToList();
        foreach (var claim in identityClaims)
        {
            sessionClaims.Add(new ClaimModel() { ClaimType = claim.Type, ClaimValue = claim.Value });
            identity.RemoveClaim(claim);
        }

        this.contextAccessor.HttpContext.Session.SetString("IdentityClaims", JsonConvert.SerializeObject(sessionClaims));

        return principal;
    }
}

The Identity system should be configured to use our ApplicationSignInManager instead of the default one. For this we need to define the dependency injection in the Startup under ConfigureServices

services.AddScoped<SignInManager<ApplicationUser>, ApplicationSignInManager>();

As you see with above code claims are removed from the principal identity and stored in the session. These claims should to be added back to the principal identity for every request. This done through the claims transformer as shown below:

public class ClaimsTransformer : IClaimsTransformer
{
    public Task<ClaimsPrincipal> TransformAsync(ClaimsTransformationContext context)
    {
        ClaimsPrincipal principal = context.Principal;
        ClaimsIdentity identity = (ClaimsIdentity)principal.Identity;
        string claimString = NTContext.HttpContext.Session.GetString("IdentityClaims");
        if (claimString != null)
        {
            List<ClaimModel> sessionClaims = JsonConvert.DeserializeObject<List<ClaimModel>>(claimString);
            identity.AddClaims(sessionClaims.Select(sc => new Claim(sc.ClaimType, sc.ClaimValue)));
        }

        return Task.FromResult(principal);
    }
}

This ClaimsTransformer class is configured in the Startup under Configure section as shown below:

app.UseClaimsTransformation(new ClaimsTransformationOptions()
{
    Transformer = new ClaimsTransformer(),
});

app.UseIdentity();


With the above setup now the claims are stored in session when the user logs in. At every request from the user these claims will be copied from session to the identity object. Now we have overridden the default approach of using cookies with session J

10 comments:

  1. Oh, they are really hard to distinguish from each other! Thanks for the help

    ReplyDelete
  2. The saving all applications is optimal in each case. However, this also has a limit. The users will be able to leave their requests for review.

    ReplyDelete
  3. This comment has been removed by the author.

    ReplyDelete
  4. I have read a few of the articles on your website now, and I really like your style of blogging. I added it to my favourites blog site list and will be checking back soon.
    Click here:
    angularjs training in chennai
    Click here:
    angularjs2 training in chennai

    ReplyDelete
  5. A good blog always comes-up with new and exciting information and while reading I have feel that this blog is really have all those quality that qualify a blog to be a one.I wanted to leave a little comment to support you and wish you a good continuation. Wishing you the best of luck for all your blogging efforts read this.
    Click here:
    Microsoft azure training in chennai
    Click here:
    Microsoft azure training in online

    ReplyDelete
  6. Thanks you for sharing this unique useful information content with us. Really awesome work. keep on blogging
    Devops training in velachery
    Devops training in annanagar

    ReplyDelete
  7. Fake IDs can be awesome fun as long as you utilize them legitimately. http://idhurry.com/ Numerous individuals imagine that fake ids are illicit, yet for the most part this isn't the situation.

    ReplyDelete
  8. A very nice guide. I will definitely follow these tips. Thank you for sharing such detailed article. I am learning a lot from you.

    rpa training in electronic-city | rpa training in btm | rpa training in marathahalli | rpa training in pune

    ReplyDelete
  9. It would have been the happiest moment for you,I mean if we have been waiting for something to happen and when it happens we forgot all hardwork and wait for getting that happened.
    java training in jayanagar | java training in electronic city

    java training in chennai | java training in USA

    ReplyDelete