IsPersistent not working - Cookie only valid for current session - c#

I have an ASP.NET MVC 5 application using ASP.NET Identity 2.1.0 for user authentication.
Everything worked fine in the past, but now I found out that persisting user sessions does not work anymore. I can not tell what change broke this, but it worked when I implemented Identity (converted the application from SimpleMembership) and this is my logic I have at the moment:
var result = await SignInManager.PasswordSignInAsync(model.UserName, model.Password,
model.RememberMe, shouldLockout: true);
SignInManager is my ApplicationSignInManager based on SignInManager<ApplicationUser, int> and model.RememberMe is true.
And my setup:
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = ApplicationCookieIdentityValidator.OnValidateIdentity(
validateInterval: TimeSpan.FromMinutes(0),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));
app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
Everything works fine, except persisting the user sessions. I checked the cookies returned by my server and the .AspNet.ApplicationCookie is allways returned as "valid for current session" instead of some date in the future. So when I close and reopen the browser I need to log in again...
Does anybody have an idea why this is not working (anymore)?
P.S.: I have overriden SignInAsync in my ApplicationSignInManager because I do some custom logic there, but I even checked with the debugger and for the following call:
await base.SignInAsync(user, isPersistent, rememberBrowser);
isPersistent is true, so it should create a persisten cookie.

This is a known bug in Identity and by looking on this answer it is not very new.
When cookie is regenerated on every request, "IsPersisted" flag is not set, when, even when it was set in the original cookie.
To work around this, you will need to implement your own version of cookie validator that will set the flag as is should.
I think I've got the solution for you, but I have not compiled or tested it - just merely a general direction of where you need to go. See this gist for full code.
This is just a SecurityStampValidator code taken from decompiler. I've added lines 91-96. Basically I take "IsPersistent" flag from the previous cookie and add it to the new cookie, when it is created. That was not done in non-modified version.
And then in your Auth.Config you do:
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = MySecurityStampValidator.OnValidateIdentity(
validateInterval: TimeSpan.FromMinutes(0),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
Beware though, when new version is out, check if this was fixed, so you can remove the dirty fix. This issue is reported to be fixed, but shortly after v2.1 was out.

Updating both AspNet.Identity.Core and AspNet.Identity.Owin to 2.2.1 should resolve this issue.

In order to keep a user loggedin on browser close in Mvc Indentity. The below code is what worked for me in Startup.Auth.cs class.
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
SlidingExpiration = true,
CookieHttpOnly = false,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) =>
user.GenerateUserIdentityAsync(manager)),
OnResponseSignIn = context => {
context.Properties.IsPersistent = true; };
}
}

Related

How to extend OWIN Cookie before expiry?

I'm trying to handle ASP.net MVC 5, timeout session scenario. Here is my code I'm using which is working as well for session expiry.
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
ExpireTimeSpan = 200,
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Home/Index"),
SlidingExpiration=true,
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = ApplicationCookieIdentityValidator.OnValidateIdentity(
validateInterval: TimeSpan.FromMinutes(1),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
I have to extend/override this expiry whenever user is shown a popup for the expiry of the session. Is there any workaround of overriding or extending the timeout just before it's actual expiry. I need a solution which can be integrated across all the application, Maybe 1 ajax centralized call to server to do that or any other options if there is any?
Thanks.

ASP.NET MVC - Same authentication between two applications

I have created two MVC applications that share the same authentication. In the applications I'm using different user roles that can be assigned to every user. When I log in as an administrator, everything works fine, I log in to the first application and the same cookie is used to log in to the second application, no login prompts involved.
When I log in as a user with a different role assigned to them, the login screen pops up again after logging in to the first application and it doesn't go away, even if I log in there also.
The applications are both on the same IIS server. The machine key is configured correctly in IIS server (obviously, since it works if I log in as a user with the administrator role assigned) and here is the code in Startup.Auth.cs for both applications:
1st Application:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
CookieName = "DefaultCookie",
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))
}
});
2nd Application:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
CookieName = "DefaultCookie",
AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Active,
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)),
OnApplyRedirect = ApplyRedirect
},
});
private static void ApplyRedirect(CookieApplyRedirectContext context)
{
Uri absoluteUri;
if (Uri.TryCreate(context.RedirectUri, UriKind.Absolute, out absoluteUri))
{
var path = PathString.FromUriComponent(absoluteUri);
Trace.WriteLine(path);
if (path == context.OwinContext.Request.PathBase + context.Options.LoginPath)
context.RedirectUri = "/Account/Login" +
new QueryString(
context.Options.ReturnUrlParameter,
context.Request.Uri.AbsoluteUri);
}
context.Response.Redirect(context.RedirectUri);
}
Does anyone know why this is happening and what I can do to fix it?
This is an authorization issue, not an authentication issue. If you can share the login at all, i.e. in the case of your admin user, then everything is fine on that front. However, the user's role must be authorized to access the controller/action or they will still be redirected to the login page, even though they are already authenticated. This is intended as an opportunity for them to re-authenticate with an account with the appropriate privileges, since the one they used apparently does not have access.
Long an short, you need to ensure that whatever controller(s)/action(s) you want the user to be able to access allow the role that is assigned to them.

ASP.NET 5 MVC6 User.GetUserId() returns incorrect id

I have simple project in ASP.NET 5 with one registered user. I have tried to get id of current logged user by extension method GetUserId() from using System.Security.Claims namespace.
Unfortunately this method returns me not existing id and i don't know why.
Here is my simple code:
var currentUserId = User.GetUserId();
Method result:
Database:
More than likely you're getting an old auth ticket that is still valid OR your getting an authentication ticket for another site your running locally. I would clear the browser cache and cookies and run it again.
Also, Try changing the name of the cookie, by default it is set to .AspNet.Cookies which can cause issues if you're running multiple apps locally. This caught me up a while back.
The cookie name can be changed like this
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<UserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(0),
regenerateIdentity: (manager, user) => manager.GenerateUserIdentityAsync(user))
},
CookieName = "myAuthCookie",
});
The MSDN documentation on it can be found here.

AuthenticationManager.GetExternalLoginInfoAsync() always null

ExternalLoginInfo loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
Why must this line always return null? It happens when the user tries to login using ANY provider. I know the FLOW is correct because actually it works locally, but when I deploy the website it always returns null.
From what I understand it's using an external cookie to track this and I can be sure it's not the browser because it works locally on the same browser.
This happens for ALL providers, even though I've double checked that all of them have the domain as "allowed" in each respective control panel. Like I said everything works locally.
I've tried restarting IIS as some answers suggested.
Also have these lines in Startup.Auth.cs with the cookie domian correctly set
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
CookieDomain = ".example.com",
...
});
//use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
Make sure your (facebook, twitter etc.) provider settings are not overriden on deployment.
Also make sure that your callback url is correctly registered at the provider. I'm using LinkedIn which needs a full url to your callback, eg. https://domain.com/signin-linkedin
I have the following setup, which works without any problems:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
CookieSecure = CookieSecureOption.SameAsRequest,
CookieName = "CustomCookieName",
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, User>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => Some.Custom.Identity.Helper.GenerateUserIdentityHelperAsync(user, manager)) // This helper method returns ClaimsIdentity for the user
}
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);

How do I forcefully propagate role changes to users with ASP.NET Identity 2.0.1?

I've read this and while it explains how role changes will eventually propagate to the user cookie after some time interval, I still don't understand how I force an immediate change to user roles.
Do I really have to sign the user out when I change his roles as administrator? If so — how? If I use AuthenticationManager.SignOut(); then I sign off myself (admin), not the user, whose roles I want to change.
Currently I use await UserManager.UpdateSecurityStampAsync(user.Id); to generate a new security stamp, but it does not work. When I refresh a page in another browser while logged in as another user his claims (including security stamp) do not change.
If you want to enable immediate revocation of cookies, then every request must hit the database to validate the cookie. So the tradeoff between delay is with your database load. But you can always set the validationInterval to 0.
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.FromSeconds(0),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});

Categories