Session cookie still authenticates after app pool recycle - c#

My ASP.NET MVC websites are staying signed in when I don't believe they should be.
My web server is IIS 7.5 and the app pool is V4.0 Integrated with idle-timeout set to 20 mins.
When logging in, the website saves an auth cookie that expires "When the browsing session ends" (persistence set to false).
If I close the browser (Chrome or FF) and then recycle (or stop/start) the app pool on IIS on the server, then reopen the browser, then session cookie still appears to be valid.
I know that Firefox and Chrome don't delete session cookies when you close the browser (so you can start where you left off), but surely the session doesn't exist on the server anymore, since the app pool has recycled? I'm not saving session data to a database, and don't have machine id's setup in my web.config.
Why is this cookie still valid?
Edit 1 - how is the cookie set
Cookie auth options are set by ConfigureAuth (called from the Configuration method in my OWIN Startup class). Cookie options:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
CookieName = "MyCookieName",
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
SlidingExpiration = true,
ExpireTimeSpan = TimeSpan.FromDays(7) // this only applies if persistent is true!
});
The user is signed in using
await SignInManager.SignInAsync(user, isPersistent, rememberBrowser);
isPersistent is set from the "Remember me" check box on the login form. If this is checked then the cookie expires using the ExpireTimeSpan from the CookieAuthenticationOptions. If it's not checked, the the cookie expires "When the browsing session ends", which is where my question lies.
rememberBrowser is set to false - this is used for 2FA which is not used in my implementation.
SignInAsync is the default implementation, which calls:
AuthenticationManager.SignIn(new AuthenticationProperties()
{
IsPersistent = isPersistent
}, userIdentity);
...which is again the default implementation.
The AuthenticationManager takes care of setting the actual cookie (I believe).
Edit 2 - machineKey?
Having looked into this further, it's possible that a machine key is being stored to the cookie, allowing it to authenticate across pool recycles. I have not defined a machineKey in the web.config, so I'm not sure this is correct.

IIS identifies applications with a machine key that stays the same across app pool recycles. This is stored in the authentication cookie so that users can be authenticated across app pool recycles.

Related

ASP.NET Identity 2 - logout not working when sign in on foo.com and logout on username.foo.com

I'm working on a multi tenant web application and primarily using .NET Framework 4.6 (MVC and ASP.NET Identity 2.0). Here's my implementation:
User visits foo.com to login. I am using following code in foo.com Startup.Auth.cs:
var cookieOptions = new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/account/login"),
CookieDomain = ".foo.com"
};
app.UseCookieAuthentication(cookieOptions);
And exact same machine key on both applications (foo.com as well as username.foo.com), here's my sample machine key:
<machineKey validationKey="xx" decryptionKey="xx" validation="SHA1" decryption="AES" />
To login i'm using following code:
signInManager.SignIn(user, isPersistent: false, rememberBrowser: false);
My other application username.abc.com is multi tenant, i.e. username can be anything. I'm using below code in username.abc.com Startup.Auth.cs:
var cookieOptions = new CookieAuthenticationOptions
{
ReturnUrlParameter = "redirectto",
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/account/login")
};
app.UseCookieAuthentication(cookieOptions);
Notice, i'm not using cookies domain, because it can be anything or maybe user has started using his own domain (let's consider user is still using foo.com subdomain).
With this code, user is successfully login and redirected on his username.foo.com, but as soon as he clicks on logout on username.foo.com, page just reloads and nothing happens. Here's what i'm using in logout action:
authenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
//authenticationManager.SignOut();
We have another option to login and logout from username.foo.com too, so when user login directly from username.foo.com, he can logout successfully. Issue occurring only when user login from foo.com.
From infrastructure point, foo.com is not load balanced but username.foo.com is running through load balancer (on production). But i don't think this will be issue, because i'm running both applications on single staging environment with same issue.
I tried custom CookieAuthenticationProvider implementation also, but it has similar issue.
Please help.
You can not change a cookie for .foo.com from username.foo.com. It's in the RFC2109.
4.3.2 Rejecting Cookies
A Set-Cookie from request-host y.x.foo.com for Domain=.foo.com
would be rejected, because H is y.x and contains a dot.
You must change your workflow. you can redirect your users to foo.com with redirect_url in order to signout and after successful signout redirect them to redirect_url which is in username.foo.com.

Does SignInAsAuthenticationType allow me to get an OAuth token without overwriting existing claims?

I need a user to login to a website using out of the box authentication to Facebook. I now need to link to the users drive in Google (and other services). I want to use ASP.Net Identity OAuth Identity providers to handle the token exchange, BUT I don't want it to touch an existing UserCredential or use it for SignIn of the UserPrincipal
My goal is to prevent
AuthenticateCoreAsync from returning a AuthenticationTicket that results in modifications to the current logged in user identity
A user shortcutting the authentication system using claims obtained from Google. (I should already have the user logged in via other means)
Prevent an unexpected token/cookie from being used to create a valid session, creating a privilege escalation scenario?
Question
What effect does setting a custom grantIdentity have on IOwinContext.Authentication.SignIn()?
Does SignInAsAuthenticationType solve my need?
If not, when would this be used?
Theoretical code using Google provider
// The cookie needs to be First in the chain.
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "CustomExternal",
AuthenticationMode = AuthenticationMode.Passive,
CookieName = "MyAwesomeCookie",
ExpireTimeSpan = TimeSpan.FromMinutes(5),
//Additional custom cookie options....
});
//Note that SignInAsAuthenticationType == AuthenticationType
app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions
{
AuthenticationType = "GoogleBackOffice",
ClientId = "abcdef...",
ClientSecret = "zyxwv....",
SignInAsAuthenticationType = "CustomExternal"
});
The Visual Studio 2015 MVC Individual User Accounts template does something like this. You create your first account (with a local username and password or a remote provider), and then you can link other identities to that one. It does this linking by maintaining two cookies during the login, one for the app and one for the external identity.
If you look in the ManageController under LinkLoginCallback, you should be able to tweak the logic at that point to store the external identity tokens, but not grant it login access to your application.
In other words, logic like this should be managed at the authorization layer in your controller logic, not at the authentication layer in the auth middleware.
If ClaimsPrincipal.Identity.IsAuthenticated is false then user will be challenged with login page.

OWIN Cookie authentication not working on IIS 8.5

I have developed an ASP.NET webapp with OWIN authentication, which works fine on my development machine (Windows 10/IIS 10), but when the webapp is published to my Windows 2012 server with IIS 8.5, the cookie authentication does not seem te work.
When I login (with the IsPersistent setting to true) and close the browser, I am still logged on when I start my browser again, so that's OK. But when I restart IIS and startup the browser, I have to logon again.
I have created a very simple application to test this, with the following code:
Startup.cs
public void ConfigureAuthentication(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Login"),
CookieName = "ThisIsTheAuthCookie"
});
}
AuthenticationController.cs
public ActionResult Login(string userName, string password)
{
//For testing purposes every user/pwd is fine
var identity = new ClaimsIdentity(new [] { new Claim(ClaimTypes.Name, userName), },
DefaultAuthenticationTypes.ApplicationCookie,
ClaimTypes.Name, ClaimTypes.Role);
HttpContext.GetOwinContext().Authentication.SignIn(new AuthenticationProperties { IsPersistent = true }, identity);
return RedirectToAction("index", "home");
}
Even Chrome shows the cookie, but it looks like OWIN is not using it on IIS 8.5:
Does anybody have an idea what the problem is?
Thx,
Danny
Can you try a couple of things and share the results :-
1. Restart the IIS , keeping the User-Agent. See if you are logged in now.
2. Enable logging in Katana and check for this warning/error in the logs.
Any result on this already?
For me it looks like you have the cookie with the session ID available but the IIS server is not aware anymore on this session. Are you sure you persist the session on the IIS server? (and not 'In Process')
You can find the option under Session State in the IIS configuration. See TechNet Article IIS
The problem is solved. I had to add the MachineKey element in the web.config!

Persistent AuthCookie is set but being redirected to login

I'm having problems with using a persistent AuthCookie. The validation and login works perfectly and if i close the browser and re-open it the authentication is still valid no redirect to the login page is done.
I'm not sure what the exact time is but let's say that if close the browser without logging off and only reopen it 20 minutes later I'll be redirected to the login page even though the cookie is set when I check with web developer tools and it's expiration date is one month from now.
All i'm doing after validating the users credentials is
FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
And in my Web.Config I have it set like
<configuration>
<system.web>
...
<authentication mode="Forms">
<forms cookieless="UseCookies" loginUrl="~/Utilizador/Login" name="SMOAuth" slidingExpiration="true" timeout="43829"/>
</authentication>
...
Also tried to hard-code the machine key as suggested here and a few other places, but to no effect
<machineKey validationKey="Validation_Key_Here" decryptionKey="Decrypt_Key_Here" validation="SHA1" decryption="AES"/>
I'm having a hard time trying to figure this out
//this line is NOT ENOUGH for "remember me" to work!!!
FormsAuthentication.SetAuthCookie(userName, true); //DOESN'T WORK!
//###########
//you have to save the "remember me" info inside the auth-ticket as well
//like this:
DateTime expires = DateTime.Now.AddDays(20); //remember for 20 days
//create the auth ticket
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1,
userName,
DateTime.Now,
expires, // value of time out property
true, // Value of IsPersistent property!!!
String.Empty,
FormsAuthentication.FormsCookiePath);
//now encrypt the auth-ticket
string encryptedTicket = FormsAuthentication.Encrypt(ticket);
//now save the ticket to a cookie
HttpCookie authCookie = new HttpCookie(
FormsAuthentication.FormsCookieName,
encryptedTicket);
authCookie.Expires = expires;
//feed the cookie to the browser
HttpContext.Current.Response.Cookies.Add(authCookie);
Check your IIS settings.
1) By default IIS 7 (and IIS 8 as well if memory serves) generates the unique encryption key per application pool at runtime. Generation at runtime means that the key is regenerated whenever app pool restarts. Meaning the persistent cookie generated before app pool restart won't be decrypted after the app pool restart, user won't be able to authenticate with the old cookie and will be taken to the login page.
2) IIS has default idle timeout - 20 minutes. Meaning that if the app does not receive a single request in 20 mins, the app pool will shut down. Then it will start anew when a request comes in.
The combination of the above two settings can lead to the behavior that you described.
PS. you may also want to check the Application event log - if it is indeed decryption failing, you will have an exception in there - something in lines of "Unable to validate data"

Force user to re enter credentials with ws-federation and Azure AD

I use ws-federation with Azure AD in my web application. All is working except that i would like my users to be logged out after 30 minutes of inactivity.
Im using cookieauthentication:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ExternalCookie,
SlidingExpiration = true,
ExpireTimeSpan = new TimeSpan(0, 30, 0),
});
And my wsfederation:
app.UseWsFederationAuthentication(
new WsFederationAuthenticationOptions
{
Wtrealm = _appSettings.Realm,
MetadataAddress = _appSettings.Metadata,
AuthenticationMode = AuthenticationMode.Passive,
SignInAsAuthenticationType = DefaultAuthenticationTypes.ExternalCookie,
UseTokenLifetime = false,
});
The user is logged out of the web application after 30 minutes. But when they click the login url and gets redirected to Azure AD they're still logged in and gets automatically signed in to my application again.
I want the users to re enter their credentials before getting signed in again. Is there a way to achieve this?
Regards
You can change the lifetime of the Azure AD SSO ticket. The default is 480 minutes I think. Just set that to 30 and the users will have to re-authenticate.
Edit: Possibly you can set Force user to enter credential on Azure AD, since this is supported in on-prem ADFS, it might be in AAD, too.
If you do not want to change the global TTL of the SSO tickets you could also send the user to the logout endpoint of the Azure AD if your local TTL expires (but that can be avoided by the user, if he want's to - so you should stick with option 1).

Categories