Asp.NET Identity setting persistent cookie to expire after specific time - asp.net

I have written a following code like below to refresh user roles after they subscribed to my website like following:
private void RefreshUserRoles()
{
var AuthenticationManager = HttpContext.GetOwinContext().Authentication;
var Identity = new ClaimsIdentity(User.Identity);
Identity.RemoveClaim(Identity.FindFirst(ClaimTypes.Role));
Identity.AddClaim(new Claim(ClaimTypes.Role, "Subscriber"));
AuthenticationManager.AuthenticationResponseGrant = new AuthenticationResponseGrant(
new ClaimsPrincipal(Identity),
new AuthenticationProperties { IsPersistent = true}
);
}
Please note this line of code:
AuthenticationManager.AuthenticationResponseGrant = new AuthenticationResponseGrant(
new ClaimsPrincipal(Identity),
new AuthenticationProperties { IsPersistent = true}
);
After user comes back to my website I set the cookie to be persistent, but I forgot to set the expiration for this cookie. I should set this cookie to last for 30 minutes, after which user should be asked to re-log onto the system.
Since some users never re-log on website, this leaves me with an issue, now I need to reset all users cookies in their browsers when they access the website and change their role if they cancelled the subscription. I noticed some users cancelled their subscription and never relogged but yet they still are able to use features on my website...
So my questions are:
How to set expiration of cookie to 30 minutes after which user will be asked to re-log onto the system.
How to Setup to re-check users cookies if they haven't expired in a long time so that I can reset their cookies to regular user if they cancelled subscription?

Here is the ConfigureAuth method I was talking about:
public void ConfigureAuth(IAppBuilder app)
{
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Controller/Action"),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));
app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
}
That's how I have it set and it works for me.

Related

ASP.NET MVC 5 OWIN Cookie Expiration

I am using OWIN middleware in my web app to manage logins/claims and issuing a cookie. The initial requirement was that the user should automatically be logged out when the browser was closed. That was easy enough, and accomplished by setting AuthenticationProperties.IsPersistent = false.
A new requirement came in that not only should the user be automatically logged out upon browser close, but they should also timeout after one hour of inactivity. I updated my ConfigureAuth method within my Startup class to provide an ExpireTimeSpan and set SlidingExpiration = true. To make those changes work, I then set AuthenticationProperties.IsPersistent = true.
Those changes made the user timeout after an hour, but now, since the cookie is persistent, the user is no longer automatically logged out upon browser close. Is there anyway to get both of these functionalities? I want my cake and eat it too!
Code used for reference:
public partial class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
ExpireTimeSpan = TimeSpan.FromHours(1),
SlidingExpiration = true,
LoginPath = new PathString("/Consent/Index"),
ReturnUrlParameter = "returnUrl"
});
}
}
and
private void UpdateClaims(User user)
{
var claims = new List<Claim>();
claims.Add(new Claim(BlahBlahBlah));
claims.Add(new Claim(MoreBlahBlah));
var identity = new ClaimsIdentity(claims, DefaultAuthenticationTypes.ApplicationCookie, ClaimTypes.Name, ClaimTypes.Role);
Authentication.SignIn(new AuthenticationProperties
{
IsPersistent = true
}, identity);
}

GetExternalLoginInfoAsync() loginInfo return null - but only after a few hours

I'm using Strava as my external login provider (I assume this is not related to Strava, could be google or facebook also) After running for a few hours / days or even weeks GetExternalLoginInfoAsync return null. I've read a bunch of other questions with the same problem, but did not find a solution. I post my entire ConfigureAuth method, just in case I did something wrong with the order.
If you have a strava account you could probably experience the problem here: fartslek.no/Account/Login
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context, user manager and signin manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
// Configure the sign in cookie
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
// Enables the application to validate the security stamp when the user logs in.
// This is a security feature which is used when you change a password or add an external login to your account.
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
},
CookieManager = new SystemWebCookieManager()
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));
app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
app.UseStravaAuthentication( new StravaAuthenticationOptions{
ClientId="XXX",
ClientSecret= "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
});
}
I'm using this https://github.com/Johnny2Shoes/Owin.Security.Strava to get StravaAuth.
When it stop working a azure reset is not enough, but if I do a new deploy everything works for a while.
I'm using Owin 3.0.1 and Mvc 5.2.3
I had the same problem. After googling a little, I've discovered this is a known bug in Owin, because of the way they handle cookies.
This issue was submitted to Katana Team, but it looks they won't fix it at all. There are many workarounds for this, but this was the simplest I could find:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult ExternalLogin(string provider, string returnUrl)
{
ControllerContext.HttpContext.Session.RemoveAll();
// Request a redirect to the external login provider
return new ChallengeResult(provider, Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl }));
}
See this question for more details about this bug, and let me know if this works well for you.

Asp.net MVC when to reload claims when lost

I have added a few custom claims when logging in.
private async Task SignInAsync(ApplicationUser user, bool isPersistent)
{
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
var accesses = _roleAccessRepository.GetAssignedAccess(roleId);
foreach (var access in accesses)
{
identity.AddClaim(new Claim("AccessCode", access));
}
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
}
They work fine until I recently found that after certain inactivity, I assume of more than 30 minutes, these claims are lost. However the user is still logged in.
How do I reload these claims when the user is still logged in?
Update: How it occured.
I logged in to a user and then after roughly 30 minutes opened it again, and logged out of it. Instead of being logged out it refreshed my page, with no claims.
You are facing SecurityStampValidator in action. In the standard MVC5 template from VS2013/VS2015 there is a file App_Start\Startup.Auth.cs that has these lines:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
// Enables the application to validate the security stamp when the user logs in.
// This is a security feature which is used when you change a password or add an external login to your account.
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
You need to look at SecurityStampValidator.OnValidateIdentity - this method regenerates cookie every 30 minutes (in default configuration). And it does not preserve claims added outside of user.GenerateUserIdentityAsync(manager).
So you need to find ApplicationUser class and modify GenerateUserIdentityAsync(UserManager<ApplicationUser> manager) method to include your claims. And don't add claims anywhere else.

Mixing Owin Asp.Net Identity Cookie Authentication with Owin OpenId Authentication

Does anyone know a good example of mixing Owin Asp.Net Identity Cookie Authentication (local db) with Owin OpenId Authentication (cloud)? Users could then choose to login/register with either creating new user&pass (stored in local database) or via e.g. Office 365 account. But all users will use the claims and roles in the asp.net Identity (local database).
I have done it like this, but I have some weird issues. Here is my ConfigureAuth method in Startup.Auth.cs
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context and user manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
//app.Properties["Microsoft.Owin.Security.Constants.DefaultSignInAsAuthenticationType"] = "ExternalCookie";
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
// Configure the sign in cookie
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
// LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = authority,
PostLogoutRedirectUri = postLogoutRedirectUri
});
}
Logoff method in AccountController
public ActionResult LogOff()
{
//AuthenticationManager.SignOut();
AuthenticationManager.SignOut(
DefaultAuthenticationTypes.ExternalCookie,
DefaultAuthenticationTypes.ApplicationCookie,
OpenIdConnectAuthenticationDefaults.AuthenticationType,
CookieAuthenticationDefaults.AuthenticationType
);
return RedirectToAction("Login", "Account");
}
Here is the issue, I tried to explain on another thread which hasn't got any response yet.
Link for the question

Identity cookie loses custom claim information after a period of time

I am storing custom claims, such as the user's real name, in the ASP.NET Identity cookie to avoid unnecessary database queries on every request. At least that's what I assume this code is doing:
var identity = await user.GenerateUserIdentityAsync(UserManager);
identity.AddClaim(new Claim(ClaimTypes.GivenName, user.FirstName)));
// etc.
AuthenticationManager.SignIn(new AuthenticationProperties {IsPersistent=true}, identity);
This works fine, and I can retrieve these claims with:
private static string GetClaim(string claimType)
{
var identity = (ClaimsPrincipal) Thread.CurrentPrincipal;
var claim = identity.Claims.SingleOrDefault(o => o.Type == claimType);
return claim == null ? null : claim.Value;
}
The identity.Claims property contains the following claims, as expected:
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier: ced2d16c-cb6c-4af0-ad5a-09df14dc8207
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name: me#example.com
http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider: ASP.NET Identity
AspNet.Identity.SecurityStamp: 284c648c-9cc7-4321-b0ce-8a347cd5bcbf
http://schemas.microsoft.com/ws/2008/06/identity/claims/role: Admin
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname: My Name
The trouble is that after some time (usually several hours), my custom claims seem to disappear - in this example, givenname no longer exists in the enumeration. The user is still authenticated, and all the default claims are still there.
What's going on, and how can I fix this? The only thing I can think of is that the cookie is expiring and being re-issued behind the scenes, but I don't know why (or if) that would happen.
Yes, the issue is most likely the cookie getting expired. Since you didn't add the custom claims to the user's claims in the database, they are lost on refresh since you aren't adding the claim inside the method being called. You can either add the claim via:
userManager.AddClaim(user.Id, new Claim(ClaimTypes.GivenName, user.FirstName));
or you can move this inside of the method that's called when the cookie is regenerated (by default its user.GenerateUserIdentityAsync).
app.UseCookieAuthentication(new CookieAuthenticationOptions {
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider {
// Enables the application to validate the security stamp when the user logs in.
// This is a security feature which is used when you change a password or add an external login to your account.
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
In Net5 you can get old cookie Custom Claim and add to new cookie like this,
builder.Services.Configure<SecurityStampValidatorOptions>(options =>
{
// set time how oftern the cookie is invalidated
options.ValidationInterval = TimeSpan.FromMinutes(10);
options.OnRefreshingPrincipal = context =>
{
// Get my custom claim from old cookie
var guidClaim = context.CurrentPrincipal.FindFirst("GUID");
// Add value to new cookie
if (guidClaim != null)
context.NewPrincipal.Identities.First().AddClaim(guidClaim);
return Task.FromResult(0);
};
});

Resources