AzureAD and OpenIdConnect session expiration in ASP.net WebForms - asp.net

I have an ASP.net WebForms application, which I have configured to work with Azure Active Directory and OpenIDConnect based on this article:
http://www.cloudidentity.com/blog/2014/07/24/protecting-an-asp-net-webforms-app-with-openid-connect-and-azure-ad/
Generally speaking, it works fine. But sometimes, after a longer period of inactivity (e.g. filling out a big form), the user is redirected to the main page of the app and his data from the form is lost. This is of course very confusing.
It seems that at some point the session (token?) expires and it needs to re-authenticate. I was able to track a HTTP 302 to https://login.windows.net/... at the point of the redirect. So it would confirm my assumption about re-authentication via AAD.
I cannot exactly tell how often this happens - for me it does not seem to be in regular intervals. I was trying to reproduce the behavior by deleting the cookies AspNet.Cookies and ASP.NET_SessionId but it did not cause the redirect. Interacting with the application just recreated those cookies automatically.
My questions are:
Is there some other location (local storage?) where the information about the login is saved?
How can I (silently, in the background) make sure that the session/token is valid?

In your initialisation code, assuming you've followed the instructions in the article you've linked, there's a line like so,
app.UseCookieAuthentication(new CookieAuthenticationOptions());
When delegating authentication to an Identity Provider, your application still drops some cookies based on the authentication result that control lifetimes. You can tweak these settings to your requirements....e.g.
appBuilder.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType ="Cookies",
ExpireTimeSpan = TimeSpan.FromMinutes(60), // Expire after an hour
SlidingExpiration = true // use sliding expiration..
});
Have a look at the docs for the things you can configure - https://learn.microsoft.com/en-us/previous-versions/aspnet/dn385599(v%3Dvs.113)
Also, an event will be raised whenever it thinks it needs to interact with the Identity Provider, handler optoions available under the Notifications object on OpenIdConnectAuthenticationOptions. One you might be interested in is RedirectToIdentityProvider
private void ConfigureOpenIdConnect()
{
var openIdConnectAuthenticationOptions = new OpenIdConnectAuthenticationOptions
{
// the rest of your settings....then
Notifications =
new OpenIdConnectAuthenticationNotifications
{
RedirectToIdentityProvider =
async context => await RedirectToIdentityProviderHandler(context)
},
UseTokenLifetime = false
};
}
private static Task RedirectToIdentityProviderHandler(
RedirectToIdentityProviderNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
{
// do some logging whenever you app is redirecting to the IDP
return Task.FromResult(0);
}
UseTokenLifetime is interesting here, as your identity token has an expiration time and by default the cookie lifetime in the application is the life of the token. If you want to control the cookie lifetime yourself (as per the first code snippet), set this to false here and explicitly control this yourself.
You could use a combination of overriding the identity token lifetime, a longer cookie lifetime, setting sliding expiration to true + some logging on whenever Redirects happen to the Identity Provider.
Obviously though, tweaking settings too much increases attack vector, so consider your security requirements carefully.

As you know, you will get ID token after login successfully via AAD. ID token is Jwt format with an expiration time. You can change the ID token's lifetime with powershell. Please refer to this document https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-configurable-token-lifetimes#access-token-lifetime.

Related

What is CookieAuthenticationOptions.AuthenticationType used for?

In my application's Asp.Net Identity Auth middleware setup I have
app.UseCookieAuthentication(new CookieAuthenticationOptions {
LoginPath = new PathString("/Login/"),
//AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
Provider = new CookieAuthenticationProvider {
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<MyUserManager, MyUser>(
TimeSpan.FromMinutes(30),
(manager, user) => manager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie)
),
},
});
I had copied this from another app and I just noticed that if I uncomment the AuthenticationType line, login succeeds (I get a success message in my logger written from my controller) but always redirects back to the login screen.
In the documentation for CookieAuthenticationOptions it says
The AuthenticationType in the options corresponds to the IIdentity AuthenticationType property. A different value may be assigned in order to use the same authentication middleware type more than once in a pipeline.(Inherited from AuthenticationOptions.)
I don't really understand what this means, why this would cause my login request to be redirected (after a successful login no less), nor what this option would be useful for.
This is a string and can be anything. But this is an identifier for authentication type. And you can have multiple authentication types: your DB with users, Google, Facebook, etc. As far as I remember this is added as a claim to the generated cookie on sign-in.
You need to know the authentication provider when you sign user out. If your authentication middleware is defined like this:
app.UseCookieAuthentication(new CookieAuthenticationOptions {
LoginPath = new PathString("/Login/"),
AuthenticationType = "My-Magical-Authentication",
// etc...
},
});
then to sign user out you need the same magic string: AuthenticationManager.SignOut("My-Magical-Authentication")
Also this string is passed into ClaimsIdentity when principal is created. And without AuthenticationType principal can't be authenticated because:
/// <summary>
/// Gets a value that indicates whether the identity has been authenticated.
/// </summary>
///
/// <returns>
/// true if the identity has been authenticated; otherwise, false.
/// </returns>
public virtual bool IsAuthenticated
{
get
{
return !string.IsNullOrEmpty(this.m_authenticationType);
}
}
This method IsAuthenticated is used through entire MVC code-base, with all authentication mechanisms relying on this.
Also theoretically you can sign in through multiple providers and sign out only a single one of them at a time, leaving the rest of providers still authenticated. Though I have never tried this.
Another use I just found - if you don't provide CookieName in your middleware configuration, then Options.CookieName = CookieAuthenticationDefaults.CookiePrefix + Options.AuthenticationType; (see second if statement in constructor).
I'm sure there are more places where it is used. But the most important is to provide it and be consistent with the name or you'll get subtle bugs in authentication system.
I don't know the whole answer, but I have an example on what it would be useful for.
I have a multi-tenant website: the website runs as a single instance multiple domains are linked to it. Each domain is a separate tenant (with a separate set of users). To implement a Facebook login per tenant, I needed a Facebook app per tenant. To configure this, I had to set a unique CallbackPath and a unique AuthenticationType per tenant:
var facebookOptions = new FacebookAuthenticationOptions
{
AuthenticationType = "Facebook-{tenantID}",
CallbackPath = new PathString($"/signin-facebook-{tenantID}")
}
I thought it was also used as a cookie name, but that's not the case for an external login like FacebookAuthentication. What I did notice is that this value of AuthenticationType popped up when requesting:
the IdentityUserLogin.LoginProvider via authenticationManager.GetExternalLoginInfoAsync()
the AuthenticationDescription.AuthenticationType via authenticationManager.GetExternalAuthenticationTypes() (seems logical ;-))
The IdentityUserLogin.LoginProvider for each user.Logins (similar to 1)
And last but not least: the value of AuthenticationType is stored in the database column AspNetUserLogins.LoginProvider.
If you set up a fresh asp.net solution, the standard set-up code (as opposed to the code you copied from another app) in Startup.Auth includes the line AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
This creates a cookie (with a default name of .AspNet.ApplicationCookie), which you can see if you look in your browser's active cookie list, that is used (amongst other things) to check whether or not the User is Authenticated for every request. If the cookie is not there (or the User is in some way not Authenticated), the middleware redirects to the route specified in your line LoginPath = new PathString("/Login/"),
The fact that this line is commented out in your code and your app works suggests that there is some other non-standard configuration in place in your code to authenticate the User. If you uncomment this line and login succeeds but redirects back to login, this suggests that there is some conflict between the non-standard code and the middleware that results in the middleware determining that the User is not Authenticated, and gets redirected back to the LoginPath.
I would seek out whether there is non-standard authentication code in your app and determine what that specifically does, and the answer for the conflict should present itself. General advice is not to change the standard Authentication code unless you know exactly what the implications of doing this are (and it can get complicated, with plenty of traps for the unwary).
Specifically to your question, this option is not just useful, it is fundamental to standard operation of the Identity middleware. You appear to have non-standard code in your app. If so, you should fully determine what it does (and it's implications) with regards to login security, or revert back to standard Identity code if you can.

[OAuth2 authorization server]refresh token's expire time need different with access token?

I'm learning OAuth2 via this tutorial, then I found refresh token's expire time is the same as access token, is this correct?
That's true: refresh tokens issued by the OAuth2 authorization server built in OWIN/Katana always have the same expiration date as access tokens ; even if you specify an explicit ExpiresUtc property in AuthenticationProperties when you call IOwinContext.Authentication.SignIn(identity, properties)
https://github.com/yreynhout/katana-clone/blob/master/src/Microsoft.Owin.Security.OAuth/OAuthAuthorizationServerHandler.cs#L333
That's not really convenient for the reasons #Hans mentioned but you can override this behavior in AuthenticationTokenProvider.CreateAsync (the class you use for OAuthAuthorizationServerOptions.RefreshTokenProvider):
Simply set context.Ticket.Properties.ExpiresUtc with the expiration date of your choice, and the refresh token will be issued with a different expiration date:
public class RefreshTokenProvider : AuthenticationTokenProvider {
public override void Create(AuthenticationTokenCreateContext context) {
context.Ticket.Properties.ExpiresUtc = // set the appropriate expiration date.
context.SetToken(context.SerializeTicket());
}
}
You can also take a look at AspNet.Security.OpenIdConnect.Server, a fork of the OAuth2 authorization server offered by OWIN/Katana that has a native RefreshTokenLifetime: https://github.com/aspnet-contrib/AspNet.Security.OpenIdConnect.Server/tree/dev
app.UseOpenIdConnectServer(options => {
// Essential properties omitted for brevity.
// See https://github.com/aspnet-contrib/AspNet.Security.OpenIdConnect.Server/tree/dev/samples/Mvc for more information.
// RefreshTokenLifetime allows you to define a lifetime specific to refresh tokens,
// which is totally independent of the lifetime used for access tokens.
options.RefreshTokenLifetime = TimeSpan.FromDays(14);
});
Don't hesitate to ping me if you need help.
In general that does not make much sense: the refresh_token exists to allow the Client to get a new access_token when the current one expires. If the refresh_token has also expired by then, there's nothing that a Client can do with it so it is useless.
There's one (more or less) edge case in which this is useful though: when the Resource Server actively rejects the access_token before it expires, the Client can now go back to the Authorization Server to get a new access_token.

How to track expired WIF fedauth cookies?

I have an interesting problem with trying to keep track of expired WIF authentication sessions/cookies.
As a bit of background: the site is MVC 3, uses Windows Identity Foundation (WIF) that has a trust with an ADFS server as an STS. The entire site is protected by SSL. The STS has the token expiry set to 60 minutes.
When a user signs out manually, we just simply call the SignOut method on the FedAuth module:
FederatedAuthentication.WSFederationAuthenticationModule.SignOut(false);
This of course removes the FedAuth cookies, but here's where the problem starts. If I capture those cookies with Fiddler, I can re-present them to the site within their expiry time and still be treated as logged in.
I realise that this is being performed from a privileged position of the browser having accepted fiddler as a proxy... but the customer is worried that those auth cookies not actually being expired presents a significant security risk. They're are not convinced that SSL protects the site sufficiently, and that if an attacker could execute an MITM attack, they could use those cookies after the user thinks they have logged out.
I have explained that if they are vulnerable after log out, they are vulnerable during log in, but they don't care...
So I have looked for ways to be sure that once a user logs off, the fedauth cookies associated with that logon session are treated as expired. The WIF handlers don't seem to have a built in mechanism for tracking expired tokens, and I have not found anything else related to this.
I guess that this is in fact a wider problem -> how to detect expired cookies in general? A valid cookie is a valid cookie!
The obvious solution is to track those cookies after logout somehow, but I'd like to avoid the custom code route if possible; as a noob, a lot of the security literature says to avoid custom coding any kind of session mechanics, as you will probably get it wrong!
Is anyone aware of any standard solutions in ASP.NET to this problem?
Thanks in advance.
You don't without keeping a server-side list of the tokens recently revoked. This is why normally we rely upon an inherent expiration as well as HTTPS to prevent the token from being leaked/stolen.
I was tasked with a similar request by our security team. I opted to store the asp.net session id in the OWIN cookie and on each request that contained a session id in the cookie I verify it matches the active session's Id.
Store session id in the cookie (adapted from this answer) at the end of the first request that is authenticated and doesn't already have the session id in the cookie:
protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
base.OnActionExecuted(filterContext);
bool authenticated = User.Identity.IsAuthenticated;
var sessionGuid = (User as ClaimsPrincipal).FindFirst("sessionID")?.Value;
//put the SessionID into the cookie.
if (authenticated && string.IsNullOrEmpty(sessionGuid))
{
var id= Session.SessionID;
//update the guid claim to track with the session
var authenticationManager = HttpContext.GetOwinContext().Authentication;
// create a new identity from the old one
var identity = new ClaimsIdentity(User.Identity);
// update claim value
identity.RemoveClaim(identity.FindFirst("sessionID"));
identity.AddClaim(new Claim("sessionID", id));
// tell the authentication manager to use this new identity
authenticationManager.AuthenticationResponseGrant =
new AuthenticationResponseGrant(
new ClaimsPrincipal(identity),
new AuthenticationProperties { IsPersistent = true }
);
}
}
Then on each future request if I find a session in the cookie compare it to active session. If they don't match then logout:
protected override void OnActionExecuting( ActionExecutingContext filterContext)
{
var claim = (User as ClaimsPrincipal).FindFirst("sessionID")?.Value;
//does the owin cookie have a sessionID?
if (!string.IsNullOrEmpty(claim))
{
string session = Session.SessionID;
//does it match the one stored in the session?
if(session != claim)
{
//no? log the user out again..
Session.Abandon();
//redirect to logged out page
this.Request.GetOwinContext().Authentication.SignOut();
//tell them its over..
Response.Write("Expired Session");
Response.End();
}
}
base.OnActionExecuting(filterContext);
}

What is the correct way to trigger OWIN cookie middleware set to passive authentication mode?

I have been following the OAuth 2.0 Authorization Server sample code
http://www.asp.net/aspnet/overview/owin-and-katana/owin-oauth-20-authorization-server
As well as looking at the nugget package Microsoft.aspnet.identity.samples package
(install-package Microsoft.aspnet.identity.samples -Pre)
and am trying to get my head around how passive vs. active cookie middleware works.
In the Authorization server example, the "Application" cookie is set to passive.
In the Identity samples, "ApplicationCookie" is active.
When I read about this property, it explains that passive middleware is only triggered when requested by a matching AuthenticationType.
If I edit the startup.auth.cs file in the Microsoft.aspnet.identity.samples and set the application cookie to passive, then log in, it seems to validate, but doesn't log me in.
Digging deeper into the code, I see that the account controller boil down to a call to SignInHelper.SignInAsync
This method gets a claimsidentity from the user which is a call to: CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie)
I am obviously not understanding something, since from what I read and can tell, the cookie has the same AuthenticationType as the Claim, but when the Authentication.SignIn is called, the Cookie doesn't seem to get set and I am returned to the main page with options to register and login.
To duplicate the issue, start a new project empty asp.net application, then install the Identity sample package, then change startup.auth.cs's app.useCookieAuthentication to:
app.UseCookieAuthentication(new CookieAuthenticationOptions {
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Passive,
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))
}
});
I have tried changing the cookie name in the startup.auth.cs and adding the "custom" name to the code that generates the claim to no avail.
I am going to keep researching, but thought I would reach out to the community in the meantime.
I'm not sure of the exact question you have.
OWIN is a pipeline that runs all the middleware modules registered. You can have multiple authentication types of middleware registered.
The cookie decrypts to the identity. If you change the authentication type to external bearer it will be a bearer token in the cookie.
I'm not sure why its not working for you, this is what I use. (I haven't looked at the external logins of the template)
// Enable the application to use a cookie to store information for the signed in user
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
//just to show bearer
AuthenticationType = DefaultAuthenticationTypes.ExternalBearer,
LoginPath = new PathString("/Account/Login"),
}
Good explanation of active vs. passive, from Mr. Brock Allen himself.
Active vs Passive authentication middleware
One question that arises — if the new templates have multiple OWIN authentication middleware configured, then which one is really used? Well, OWIN authentication middleware has the concept of passive vs. active. Active middleware always look at every incoming request and attempt to authenticate the call and if successful they create a principal that represents the current user and assign that principal to the hosting environment. Passive middleware, on the other hand, only inspects the request when asked to. In the case of the default templates from Visual Studio 2013, all the middleware configured are all passive by default, except for the “main” cookie authentication middleware (turns out there are two cookie middlewares that get used in some templates — the main one and another one for external identity providers and this other one is marked as passive).
I arrived here because I was asking the same question. It turns out there is no correct way because it cannot be done. The Authentication middleware calls the "Invoke" method to test whether it should do anything in passive mode. In middleware such as the OpenIdConnect middleware, this method is over-ridden such that it checks the request path, and if it's the Options.RedirectUri then the the middleware does something, sets up the Response object to do something like a redirect and returns "true" to indicate that the request has been handled, and then the middleware stack is unwound and the response processed.
The CookieAuthenticationMiddleware's Invoke method does nothing, it only returns 'false', which means that control is passed to the next middleware in the chain.
So, the result is that CookieAuthenticationMiddleware cannot be used for authentication in passive mode.
I found this blog post really helpful: https://chris.59north.com/post/Understanding-OWIN-(Katana)-Authentication-Middleware . Also, take a look at the Katana source code: https://github.com/aspnet/AspNetKatana
page works fine when i comment below coding.
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
//LoginPath = new PathString("/Login"),
Provider = new CookieAuthenticationProvider(),
CookieName = "DefaultAuthenticationTypes",
CookieHttpOnly = true,
ExpireTimeSpan = TimeSpan.FromHours(cookieActiveTime),
});
.

What is ASP.NET Identity's IUserSecurityStampStore<TUser> interface?

Looking at ASP.NET Identity (new membership implementation in ASP.NET), I came across this interface when implementing my own UserStore:
//Microsoft.AspNet.Identity.Core.dll
namespace Microsoft.AspNet.Identity
{
public interface IUserSecurityStampStore<TUser> :
{
// Methods
Task<string> GetSecurityStampAsync(TUser user);
Task SetSecurityStampAsync(TUser user, string stamp);
}
}
IUserSecurityStampStore is implemented by the default EntityFramework.UserStore<TUser> which essentially get and set the TUser.SecurityStamp property.
After some more digging, it appears that a SecurityStamp is a Guid that is newly generated at key points in the UserManager (for example, changing passwords).
I can't really decipher much beyond this since I'm examining this code in Reflector. Almost all the symbol and async information has been optimized out.
Also, Google hasn't been much help.
Questions are:
What is a SecurityStamp in ASP.NET Identity and what is it used for?
Does the SecurityStamp play any role when authentication cookies are created?
Are there any security ramifications or precautions that need to be taken with this? For example, don't send this value downstream to clients?
Update (9/16/2014)
Source code available here:
https://github.com/aspnet/Identity/
https://github.com/aspnet/Security/
This is meant to represent the current snapshot of your user's credentials. So if nothing changes, the stamp will stay the same. But if the user's password is changed, or a login is removed (unlink your google/fb account), the stamp will change. This is needed for things like automatically signing users/rejecting old cookies when this occurs, which is a feature that's coming in 2.0.
Identity is not open source yet, its currently in the pipeline still.
Edit: Updated for 2.0.0. So the primary purpose of the SecurityStamp is to enable sign out everywhere. The basic idea is that whenever something security related is changed on the user, like a password, it is a good idea to automatically invalidate any existing sign in cookies, so if your password/account was previously compromised, the attacker no longer has access.
In 2.0.0 we added the following configuration to hook the OnValidateIdentity method in the CookieMiddleware to look at the SecurityStamp and reject cookies when it has changed. It also automatically refreshes the user's claims from the database every refreshInterval if the stamp is unchanged (which takes care of things like changing roles etc)
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))
}
});
If your app wants to trigger this behavior explicitly, it can call:
UserManager.UpdateSecurityStampAsync(userId);
The UseCookieAuthentication is deprecated by now. I managed to configure it using
services.Configure<SecurityStampValidatorOptions>(o =>
o.ValidationInterval = TimeSpan.FromSeconds(10));
Moved from reply to answer per request.
I observed the SecurityStamp to be required for token verification.
To repo:
Set SecurityStamp to null in the databsae
Generate a token (works ok)
Verify token (fails)

Resources