BackGround : User once logged in to the our Web Application (using App level credential) will be presented with with Mail System they want to use based on that user will be redirected to respective authorization server to authenticate (using login / password of their mail system) and the auth server will return an access token back.
In Notification events like OnAuthorizationCodeReceivedAsync or OnAuthenticationFailedAsync; we are not getting ASP.NET_SessionId so having said that i am not able to use any of the session values which are set before OAuth Flow.
Refer below Code for more details.
app.UseOpenIdConnectAuthentication(New OpenIdConnectAuthenticationOptions With {
.ClientId = appId,
.ClientSecret = appSecret,
.Authority = "https://login.microsoftonline.com/common/v2.0",
.Scope = $"openid email profile offline_access {ewsScopes}",
.RedirectUri = redirectUri,
.PostLogoutRedirectUri = redirectUri,
.TokenValidationParameters = New TokenValidationParameters With {
.ValidateIssuer = False
},
.Notifications = New OpenIdConnectAuthenticationNotifications With {
.AuthenticationFailed = AddressOf OnAuthenticationFailedAsync,
.AuthorizationCodeReceived = AddressOf OnAuthorizationCodeReceivedAsync
}
})
I am not able to get any session values in HttpConext.Current.Session which are set before OAuth flow in notification events.
As per below SO; i tried different approaches like SystemWebCookieManager, UseKentorOwinCookieSaver but issue not resolved.
ASP.NET_SessionId + OWIN Cookies do not send to browser
What could be the issue and how can I resolve it?
ByDefault; OpenIDConnect uses form post redirects which are incompatible with SameSite. Due to that Application Session cookie not sent over and that is how it should be.
As per couple of Stack overflow link below; using either URL rewrite or below web.config allows us to maintain session when response is posted back to Callback url but we still need to use Owin's SystemWebCookieManager for that in order to work.
Browser won't set ASP.NET_SessionId cookie on payment gateway's post request to our site
how SameSite attribute added to my Asp.net_SessionID cookie automatically?
Considering above scenario; for OpenIDConnect Authentication; setting samesite cookie to none and secure; that should work but i afraid that would raise CSRF (Cross site request Forgery) vulnerability for application.
Hence, An alternative is to switch to the Code response type which uses HTTP redirects and works with SameSite=Lax. Set the appropriate code response mode and response type.
ResponseMode = OpenIdConnectResponseMode.Query;
ResponseType = OpenIdConnectResponseType.Code;
https://github.com/aspnet/AspNetKatana/blob/635c92f641ad1e014eead31cc7a365004949fda5/src/Microsoft.Owin.Security.OpenIdConnect/OpenIdConnectAuthenticationOptions.cs#L65-L66
Related
Cookie is a executable program, which the server posts to client machines but what is the timeout of the cookie
Learning the concept of cookies so this question comes to my mind.
For ASP.NET Core by default cookie set with Append(string, string) or Append(String, String, CookieOptions) with default options will result in so called session cookie which will expire once you log off or close the browser.
app.MapGet("/api/ttt", (HttpResponse resp) =>
{
resp.Cookies.Append("testcookie", "val");
resp.Cookies.Append("testcookie1", "val", new CookieOptions());
return new { Test = "ok" };
});
You can control the cookie expiration by setting MaxAge or Expires properties on CookieOptions.
Note that ASP.NET Core has several specific cookies (like auth cookies) which can have their own default lifetimes.
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.
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);
}
I need to use FormsAuthentication to authenticate login for users using ajax, I know I can't use FormsAuthentication.RedirectFromLoginPage("UserName",false) because there's no redirection when using ajax.
What can I use to set that this user authorized.
Tried to use:
FormsAuthentication.SetAuthCookie("UserName", false);
But there's cookie here and I don't want to use cookie.
Thanks
You can use the encryption/decryption/expiration aspects of Forms Authentication separately from the request/response cycle (e.g. cookies).
For example: you can get the auth token as a string like this:
// create a ticket for "myusername" which expires in 15 minutes
string authToken = FormsAuthentication.Encrypt(new FormsAuthenticationTicket("myusername", false, 15));
And then when use receive it back from the user from some custom approach (you didn't really specify what you're going to use instead of cookies) you use FormsAuthentication.Decrypt to check if it's valid. Note that you have to trap exception and check for null in order to check validity.
// decrypt receivedAuthToken string which was received somehow in your request
FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(receivedAuthToken);
But then you have to also manage the renewal process yourself too:
var newTicket = FormsAuthentication.RenewTicketIfOld(authTicket);
var newToken = FormsAuthentication.Encrypt(newTicket)
I'm setting the cookie expiration using the following code:
// remove existing cookies.
request.Cookies.Clear();
response.Cookies.Clear();
// ... serialize and encrypt my data ...
// now set the cookie.
HttpCookie cookie = new HttpCookie(AuthCookieName, encrypted);
cookie.Expires = DateTime.Now.Add(TimeSpan.FromHours(CookieTimeOutHours));
cookie.HttpOnly = true;
response.Cookies.Add(cookie);
// redirect to different page
When I read the cookie timeout in the other page I'm getting 1/1/0001 12:00 AM. If someone can help me figure out the problem, I'll appreciate it. I'm using ASP.NET 3.5
ok. after reading the links from Gulzar, it appears that I cannot check cookie.Expires on the HttpRequest at all? Because the links seem to suggest that cookie.Expires is always set to DateTime.MinValue because the server can never know the actual time on the client machine? So this means I have to store the time inside the cookie myself and check it? Is my understanding correct?
thanks
Shankar
The problem here doesn't really lie with ASP.NET but with the amount of information that is provided in the http request by browsers. The expiry date would be unobtainable regardless of the platform you are using on the server side.
As you have summarised yourself in your question the Expires property of the HttpCookie object that is provided by the HttpRequest object is always set to 1/1/0001 12:00 AM.
This is because this expiry information, as well as the properties such as domain and path, are not passed by the browser to the server when it sends a request. The only cookie information that is sent is the name and value(s). Therefore cookies in the request will have default values for these 'missing' fields as they are unknown on the server side.
I would guess the reason behind this is that the expiry, domain and path attributes of a cookie are only intended to be used by the browser when it is making a decision as to whether it should pass a cookie in a request or not and that the server is only interested in the name and value(s).
The work around you have suggested of duplicating the expiry time as another value of the cookie is a way to get the behaviour you are looking for.
At first I also disappointed why Request cookie doesn't have the actual Expires value. After debugging the http by using Fiddler2. I know that .NET was not wrong, instead, http protocol is the answer why Request cookies behaving like that.
If you use Fiddler between your app and the browser. You can see the Response cookie sent correctly to browser with all domain, path, expires, secure and httponly. However next Request cookie in http cookie header doesn't have the expires value, it only cookie name and value. Browser responsible to send this request header and I believe that is because http protocol. The reason why is because to minimize size and web server doesn't need to check it since they actually set all the values. They should notice.
So you no need to check the expires value on web request since you already know the date. Just, if you receive the cookie back that means the cookie is not yet expired. Once you set the expires, browser will handle the expiry. If you want to change the expires, just set the new value on the response.
CallMeLaNN
To solve this problem I add a claim to the principal which I can then read back and display the cookie expiry time to the user as follows:
public static void Configuration(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString(string.Format("~/Login.aspx"),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SetExpiryClaim
}
});
app.MapSignalR();
}
private static Task SetExpiryClaim(CookieValidateIdentityContext context)
{
var contextExpireUtc = context.Properties.ExpiresUtc;
var claimTypeName = "contextExpireUtc";
var identity = context.Identity;
Claim contextExpiryClaim;
if (identity.HasClaim(c => c.Type == claimTypeName))
{
contextExpiryClaim = identity.FindFirst(claimTypeName);
identity.RemoveClaim(contextExpiryClaim);
}
contextExpiryClaim = new Claim(claimTypeName, contextExpireUtc.ToString());
context.Identity.AddClaim(contextExpiryClaim);
return Task.FromResult(true);
}
Then you are able to retrieve the expiry time later from the ClaimsPrinciple by
ClaimsPrincipal principle = Thread.CurrentPrincipal as ClaimsPrincipal;
DateTime contextExpiry = principle.Claims.First(p => p.Type == "contextExpireUtc").Value.AsDateTime();
The version problem discussed in the link was not helpful. Basically ASP.NET cookie sucks. I had to store the expiration date time inside the cookie myself and check it in every request.
I had a similar problem when testing with an Iphone. The problem was due to the Iphone having the wrong date time set on the device, thus setting the cookie expire date to datetime.now.adddays(-1) did not expire the cookie
A request from a browser sends only the name and value of the cookies as follows:
GET /sample_page.html HTTP/2.0
Host: www.example.org
Cookie: yummy_cookie=choco; tasty_cookie=strawberry
So this is not a problem in ASP.NET. And as mentioned in some answers and comments the expiration date is to determine whether to send that cookie to the server or not. So if the cookie was expired, it would be null on the server.
Also the workaround would be storing another value on that cookie that determines the expiry date (if you really need it)
Reference: Using HTTP cookies