Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Force another user to refresh their Claims with ASP.NET Identity 2.1.0

I'm using Asp.NET Identity 2.1.0 and I store a list of Accounts that a User has access to, as Claims. The ClaimsIdentity is generated when the User signs in:

    public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
    var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);

    // Add Claims concerning Account
    userIdentity.AddClaim(new Claim("AccountList", SerializedListOfAccounts));

    return userIdentity;
}

Let's say that an Administrator revokes User A's access to a specific Account. How can I force User A to regenerate its ClaimsIdentity? Remember that it isn't in the context of User A. And I don't want to wait until the cookie has expired (and a new ClaimsIdentity is automatically generated.

Is it possible? Isn't there a way to tell the server to regard User A's cookie as invalid and force it to regenerate it?

The reason I want this behaviour is to create a custom AuthorizeAttribute that I can put on my controllers that checks the Claims to see if a User has access or not, to avoid an extra round trip to the database.

like image 570
Joel Avatar asked Jul 03 '15 12:07

Joel


1 Answers

You can not store their claims on cookie, but apply them on every request to the identity early in the pipeline. You'll have to hack Startup.Auth.cs to do that. I'm doing just that here.

And here is the gist you can work with:

public partial class Startup
{
    public void ConfigureAuth(IAppBuilder app)
    {
        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            Provider = GetMyCookieAuthenticationProvider(),
            // other configurations
        });

        // other usual code
    }



    private static CookieAuthenticationProvider GetMyCookieAuthenticationProvider()
    {
        var cookieAuthenticationProvider = new CookieAuthenticationProvider();
        cookieAuthenticationProvider.OnValidateIdentity = async context =>
        {
            // execute default cookie validation function
            var cookieValidatorFunc = SecurityStampValidator.OnValidateIdentity<UserManager, ApplicationUser>(
                TimeSpan.FromMinutes(10),
                (manager, user) =>
                {
                    var identity = manager.GenerateUserIdentityAsync(user);
                    return identity;
                });
            await cookieValidatorFunc.Invoke(context);

            // sanity checks
            if (context.Identity == null || !context.Identity.IsAuthenticated)
            {
                return;
            }


            // get your claim from your DB or other source
            context.Identity.AddClaims(newClaim);
        };
        return cookieAuthenticationProvider;
    }
}

The drawbacks that you need to apply claims on every request and this might be not very performant. But right amount of caching in the right place will help. Also this piece of code is not the easiest place to work, as it is very early in the pipeline and you need to manager yourself DbContext and other dependencies.

The upsides are that the claims are applied right away to every user's request and you get immediate change of permissions without having to re-login.

like image 166
trailmax Avatar answered Oct 13 '22 21:10

trailmax