Asp.net mvc identity SecurityStamp signout everywhere - c#

What I want to do is to limit a user ID to only being able to log in to one device at a time. For example, user ID "abc" logs in to their computer. User ID "abc" now tries to log in from their phone. What I want to happen is to kill the session on their computer.
I'm using Asp.net mvc identity membership and using SecurityStamp for this purpose. This is my code in Account/Login action:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
var user = UserManager.FindByEmail(model.Email);
var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
await UserManager.UpdateSecurityStampAsync(user.Id);
According to the UpdateSecurityStampAsync method doc says : Generate a new security stamp for a user, used for SignOutEverywhere functionality. But it doesn't work.

If you want to enable instant invalidation of cookies on other devices, then every request must hit the database to validate the cookie. To do that you need to configure cookie invalidation in Auth.Config.cs and set validateInterval to 0:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
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<UserManager, ApplicationUser>(
validateInterval: TimeSpan.FromSeconds(0),
regenerateIdentityCallback: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
);

Related

identity 2.2 logging off doesn't destroy session

After a pen test of our system, it was pointed out that when a user is logged out, that users session is still active, although the cookie is deleted. I have confirmed this by copying the .AspNet.ApplicationCookie value, and made a request to an otherwise restricted site with Postman. When debugging i can see that even after i abandon the session, the session persists with an ID.
This is my current LogOff method:
public ActionResult LogOff()
{
HttpContext.Response.Cache.SetAllowResponseInBrowserHistory(false);
HttpContext.Response.Cache.SetCacheability(HttpCacheability.NoCache);
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
HttpContext.Session.Clear();
HttpContext.Session.Abandon();
HttpContext.Response.Cookies.Add(new HttpCookie("ASP.NET_SessionId", ""));
return RedirectToAction("Index", "Home");
}
I wonder if this is a bug in Identity or MVC or is it me that's missing something?
I am using Identity 2.2.1 with Entity Framework 6 and MVC 5
Identity does not use session, so if you do use, it'll be your responsibility to kill session.
However, when Identity does log out, cookie is expired. But the same cookie value can be used after to be logged in. To mitigate this you can update SecurityStamp on a user:
await userManager.UpdateSecurityStampAsync(userId);
and make sure you have SecurityStampValidator activated in Startup.Auth.cs
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(1),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager)),
},
// other stuff
});
However, be aware that updating security stamp will invalidate all user cookies in all browsers. So if user is logged-in in Chrome and IE, then logs out from Chrome, IE cookie will also be invalidated.

ASP.NET Identity and validation of a custom claim

I am using ASP.NET Identity with MVC, and I set a sessionId (GuId string) on each logged in user for each one of his devices. The idea is that a user can remove device sessions, and then that device will not be logged in anymore (as it is done in dropbox, and google).
Currently, I set this sessionId as a claim in ASP.NET Identity, so it is passed in the authentication cookie.
For Authenrication I use ASP.NET Identity as the samples: app.UseCookieAuthentication(new CookieAuthenticationOptions{....
My questions:
Is setting my sessionId to the claims the right approach here?
Also, where in the whole authentication process can I validate the claim of that sessionId?
My current idea is to validate this sessionId against a database table for each request. Should I use Request.Sessions to store the sessionId instead, or any other idea here?
Thanks,
Since a user can have multiple valid sessions, you would either need to store these as claims or create your own table to store them. Since claims is already created by Identity, that will be easiest.
You'd validate this in the OnValidateIdentity method of CookieAuthenticationProvider in Startup.Auth.cs. Currently this calls the OnValidateIdentity method of the SecurityStampValidator so you need to write a wrapper method that first checks your session id and then calls the original security stamp validator. For example you could add these methods to the Startup class:
private Func<CookieValidateIdentityContext, System.Threading.Tasks.Task> _validate=SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager));
private async Task validate(CookieValidateIdentityContext context)
{
var usermanager = context.OwinContext.GetUserManager<ApplicationUserManager>();
var claims = await usermanager.GetClaimsAsync(context.Identity.GetUserId());
//instead of setting to true, add your session validation logic here
bool sessionIsValid=true;
if (!sessionIsValid) {
context.RejectIdentity();
context.OwinContext.Authentication.SignOut(context.Options.AuthenticationType);
}
await _validate(context);
}
where _validate is just the original method and validate is your new method that also checks session id. Then your app.UseCookieAuthentication code will reference the new validate method like this:
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 = validate
}
});
To make this work I think you'll need to check the claims from the database every time, but I believe the usermanager.GetClaimsAsync will ultimately do this.

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 Identity 2.0 Sign-out another user

I'm using asp.net MVC and ASP.net Identity 2.0.
On my website Admin has option to ban user, and I would like when user is banned that he is automatically signed-out from website.
I know that I can sign-out current user by calling
AuthenticationManager.SignOut();
But is it possible to sign-out another user ? Or maybe shorter his session ? Or anything ?
I know I could make global filter on controllers prohibiting banned users from access but that filter would be ran against each user so I'm not quiet satisfied with that solution.
If you use the securitystampvalidator feature, when a user is banned just call: UpdateSecurityStamp(userId) to cause any existing login cookies to be invalid the next time they are checked.
More info about SecurityStamp?
You'll need to configure cookie invalidation in Auth.Config.cs:
public void ConfigureAuth(IAppBuilder app)
{
// important to register UserManager creation delegate. Won't work without it
app.CreatePerOwinContext(UserManager.Create);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator
.OnValidateIdentity<UserManager, ApplicationUser, int>(
validateInterval: TimeSpan.FromMinutes(10),
regenerateIdentityCallback: (manager, user) => user.GenerateUserIdentityAsync(manager))
},
// other configurations
});
// other stuff
}
and then update security stamp as Hao Kung says when users are banned.
I've blogged about this recently

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