I am having trouble getting ASP Identity to refresh its Identity stored in a cookie on demand.
In the Startup.Auth.cs
file the cookie is set to regenerate as follows:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<QuizSparkUserManager, QuizSparkUser, int>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentityCallback: ((manager, user) => manager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie)),
getUserIdCallback: ((claimsIdentity) => int.Parse(claimsIdentity.GetUserId())))
}
});
However I cannot work out how to refresh the contents on User.Identity
in code, i.e. force a refresh of the identity cookie when I need it to refresh.
I want to be able to use the regenerate identity callback programmatically, is this possible?
My problem is similar to this one : How to invalidate .AspNet.ApplicationCookie after Adding user to Role using Asp.Net Identity 2?
However I want to refresh rather than invalidate the cookie.
Edit
After looking at the linked question I attempted the following (without full error handling):
IOwinContext context = Request.GetOwinContext();
QuizSparkSignInManager manager = context.Get<QuizSparkSignInManager>();
ClaimsIdentity newIdentity = manager.CreateUserIdentity(manager.UserManager.FindById(User.Identity.GetUserId<int>()));
AuthenticateResult authenticationContext =
await context.Authentication.AuthenticateAsync(DefaultAuthenticationTypes.ApplicationCookie);
if (authenticationContext != null)
{
context.Authentication.AuthenticationResponseGrant = new AuthenticationResponseGrant(
newIdentity, authenticationContext.Properties);
}
bool first2 = User.IsInRole("Turtle");
Edit2: However the User still does not appear to refresh. On page reload they do seem to refresh, am I right in thinking this is because User.Identity cookie is part of the request and cannot be changed in code?
Why do we need IdentityServer4? ASP.NET Identity can receive a security token from a third-party login provider like Facebook, Google, Microsoft and Twitter. But If you want to issue a security token for a local ASP.NET Identity user you need to work with a third-party library like IdentityServer4, OpenIddict.
If you are trying to add new role to already logged-in user, you need to sign user out. Then create new identity with new role and sign user in with the new identity. That's the only way to update the cookie.
Best place to check if user properties have changed are in callback you already use: CookieAuthenticationProvider.OnValidateIdentity
. Something like this.
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
// other stuff
Provider = new CookieAuthenticationProvider
{
// this function is executed every http request and executed very early in the pipeline
// and here you have access to cookie properties and other low-level stuff.
// makes sense to have the invalidation here
OnValidateIdentity = async context =>
{
// invalidate user cookie if user's security stamp have changed
var invalidateBySecirityStamp = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager));
await invalidateBySecirityStamp.Invoke(context);
if (context.Identity == null || !context.Identity.IsAuthenticated)
{
return;
}
if(/*Need to update cookie*/)
{
// get user manager. It must be registered with OWIN
var userManager = context.OwinContext.GetUserManager<UserManager>();
var username = context.Identity.Name;
// get new user identity with updated properties
var updatedUser = await userManager.FindByNameAsync(username);
// updated identity from the new data in the user object
var newIdentity = updatedUser.GenerateUserIdentityAsync(manager);
// kill old cookie
context.OwinContext.Authentication.SignOut(context.Options.AuthenticationType);
// sign in again
var authenticationProperties = new AuthenticationProperties() { IsPersistent = context.Properties.IsPersistent };
context.OwinContext.Authentication.SignIn(authenticationProperties, newIdentity);
}
}
}
});
Disclaimer - never tested it, not even tried to compile it.
Also can see my other answer for reference - pretty much the same piece of code, but different goal.
UPD:
Regarding another part of the question - how to detect a role change:
I can think of a way - have another GUID on a user record. Similar to SecurityStamp
, but not used by the framework. Call it MySecurityStamp
. On sign-in add value of MySecurityStamp
to the cookie as a claim. On every request compare value of MySecurityStamp
in the cookie to the value in the database. If values are different - time to regenerate the identity. And on every new role added/removed modify MySecurityStamp
for the user in the database. This will cover all the sessions in all the browsers.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With