In Asp.Net 4.0 I am writing a cookie using the HttpCookie Class. Debugging with IE9. The cookie is being saved to "/". Shouldn't it be saving to [user]\AppData\Local\Microsoft\Windows\Temporary Internet Files? When I try to find it there it does not exist. And how can I change this?
Dim cookie_name As String = "ProjectId"
Dim cookie As New HttpCookie(cookie_name)
cookie.Value = "123456789"
cookie.Expires = DateTime.Now.AddHours(8)
Response.Cookies.Add(cookie)
Debug.Print(cookie.Path.ToString) 'cookie is saving to "/"
cookie.path defines the base domain URLs the cookie applies to, its got nothing to do with the client file-system (which you don't need to worry about).
This describes how to find the cookie's physical path.
Alex K is quite correct, here's the MSDN reference for the Cookie.Path property which states:
The Path property specifies the subset of URIs on the origin server to
which this Cookie applies. If this propery is not specified, then this
Cookie will be sent to all pages on the origin server or servers.
Related
Like so many others dealing with WIF in .NET I ran into the usual error:
ID3206: A SignInResponse message may only redirect within the current
web application.
I wrote a custom authentication module where I overrode RedirectToIdentityProvider as suggested in Error - A SignInResponse message may only redirect within the current web application - MVC 2.0 application.
The suggested code sample did not take into account that the request URL might include parameters, i.e. it would simply append a trailing slash to the full URL. Eventually I expanded that code to what you see below but still used the realm in order to decide whether or not to process the URL:
Public Overrides Sub RedirectToIdentityProvider(ByVal uniqueId As String, ByVal returnUrl As String, ByVal persist As Boolean)
Dim absoluteUri As String = HttpContext.Current.Request.Url.AbsoluteUri
Dim ciAbsoluteUri As String = absoluteUri.ToLowerInvariant
Dim realm As String = MyBase.Realm
Dim ciRealm As String = realm.ToLowerInvariant
'If Realm ends with a trailing slash, the returnUrl should include a trailing slash in the same position.
'This trailing slash may or may not be the end of the returnUrl, depending on whether or not additional parameters have been provided.
If realm.EndsWith("/") Then
If Not ciAbsoluteUri.StartsWith(ciRealm) Then
Dim realmWithoutSlash As String = realm.Substring(0, realm.Length - 1)
Dim ciRealmWithoutSlash As String = realmWithoutSlash.ToLowerInvariant
If ciAbsoluteUri.StartsWith(ciRealmWithoutSlash) Then
'Realm ends with a slash and AbsoluteUri contains Realm but without a slash.
Dim absolutePath As String = HttpContext.Current.Request.Url.AbsolutePath
returnUrl = returnUrl.Replace(absolutePath, absolutePath & "/")
End If
End If
End If
MyBase.RedirectToIdentityProvider(uniqueId, returnUrl, persist)
End Sub
(This could have been written more compactly but that is besides the point.)
The code in the original post includes the following comment
//Compare if Request Url +"/" is equal to the Realm, so only root access is corrected
//https://localhost/AppName plus "/" is equal to https://localhost/AppName/
//This is to avoid MVC urls
It is perfectly possible to set the realm and audienceUri in the web.config file and the identifier on the AD FS 2.0 side to something other than a subset of the request URL as long as the value is a valid URI. For example, a bogus value of "http://corp.com" would suffice. However, using such a value would mean that the request URL would not be processed by the RedirectToIdentityProvider override.
My questions are:
What is the reason for having the realm being a subset of the request URL in an MVC application and - more importantly - is there a reason for requiring the same for a non-MVC application?
Could I not just ignore the realm and ensure that the URL includes a slash at the end (in case of no parameters) or before a list of parameters?
Just some basics: Realm is WIF language for EntityID in metadata. It should be a URI. Which means that it doesn't have to be a URL. Almost all implementations are very tolerant and do accept any string in EntityID.
Returnurl in this case means the url where the browser will go after the user has been authenticated and WIF has set the session cookie. It should be within the app (why else authenticate). If you really do want the application root then the ending slash is a good idea indeed (because of cookiepath). So the usage of Realm (in this particular case) is a mistake.
Title should say it all.
Here's the code to set the cookie:
// snip - some other code to create custom ticket
var httpCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encodedTicket);
httpCookie.Domain = "mysite.com";
httpContextBase.Response.Cookies.Add(httpCookie);
Here's my code to signout of my website:
FormsAuthentication.SignOut();
Environment:
ASP.NET MVC 3 Web Application
IIS Express
Visual Studio 2010
Custom domain: "http://localhost.www.mysite.com"
So when i try and log-off, the cookie is still there. If i get rid of the httpCookie.Domain line (e.g default to null), it works fine.
Other weird thing i noticed is that when i set the domain, Chrome doesn't show my cookie in the Resources portion of developer tools, but when i dont set the domain, it does.
And secondly, when i actually create the cookie with the custom domain, on the next request when i read in the cookie from the request (to decrypt it), the cookie is there, but the domain is null?
I also tried creating another cookie with the same name and setting the expiry to yesterday. No dice.
What's going on? Can anyone help?
I believe if you set the domain attribute on the forms element in you web.config, to the same as the one in your custom cookie, it should work. (EDIT: that approach won't work because the SignOut method on FormsAuthentication sets other flags on the cookie that you are not, like HttpOnly) The SignOut method basically just sets the cookie's expiration date to 1999, and it needs the domain to set the right cookie.
If you can't hardcode the domain, you can roll your own sign out method:
private static void SignOut()
{
var myCookie = new HttpCookie(FormsAuthentication.FormsCookieName);
myCookie.Domain = "mysite.com";
myCookie.Expires = DateTime.Now.AddDays(-1d);
HttpContext.Current.Response.Cookies.Add(myCookie);
}
An authentication cookie is just a plain cookie; so you would remove it the same way you would any other cookie: expire it and make it invalid.
I had a similar problem. In my case, I was storing some userData in the AuthCookie and experienced the same effects as described above, and upon authentication at each request, reading the cookie and putting the userData in a static variable. It turned out in my case that the data was being persisted in the application. To get around it, I had to first clear my static variable, and then expire the cookie. I used the following in the LogOff method of my AccountController:
AuthCookie.Clear(); //STATIC CLASS holding my userdata implemented by me.
Response.Cookies[FormsAuthentication.FormsCookieName].Expires = DateTime.Now.AddYears(-1);
Response.Cookies[FormsAuthentication.FormsCookieName].Value = null;
return RedirectToAction("Index", "Home");
Hope this helps.
UPDATE
On a hunch after submitting, I replaced the middle two lines with:
FormsAuthentication.SignOut();
... and it worked fine where it didn't before.
Note:
AuthCookie.Clear();
... does not touch the AuthCookie, it just resets the static class I wrote to default values.
Again, hope this helps.
When accessing my site, the user has to enter his credentials. They are basically plain directory access credentials.
At a certain point I check if a certain file they want to download exists by calling
WebRequest req = HttpWebRequest.Create(checkUri.AbsoluteUri);
WebResponse res = req.GetResponse();
Although I can access the checkUri from the browser, I get a 401 when doing the above check. I think I have to set the
req.Credentials
But I don't know where the current credentials are stored...
Any ideas?
--Update--
Integrated Windows Authentication: No
Allow Anonymous: Off
Caler: Link on page of same site (GET)
Impersonation: default is off (don't even know how to enable it in asp.net mvc)
I was having a similar problem on a site where I'm using forms authentication, I was able to solve this problem by using the code provided here as the second reply in the thread.
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(uri);
// Add the current authentication cookie to the request
HttpCookie cookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
Cookie authenticationCookie = new Cookie(
FormsAuthentication.FormsCookieName,
cookie.Value,
cookie.Path,
HttpContext.Current.Request.Url.Authority);
req.CookieContainer = new CookieContainer();
req.CookieContainer.Add(authenticationCookie);
WebResponse res = req.GetResponse();
I think you want this:
req.Credentials = CredentialCache.DefaultCredentials;
You're going to need to enable Integrated Windows Authentication.
I don't know what happens in ASP.NET MVC, but in ASP.NET Web Forms impersonation is turned on by:
<identity impersonate="true">
in web.config.
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
If there is a cookie set for a subdomain, metric.foo.com, is there a way for me to delete the metric.foo.com cookie on a request to www.foo.com? The browser (at least Firefox) seems to ignore a Set-Cookie with a domain of metric.foo.com.
Cookies are only readable by the domain that created them, so if the cookie was created at metric.foo.com, it will have to be deleted under the same domain as it was created. This includes sub-domains.
If you are required to delete a cookie from metric.foo.com, but are currently running a page at www.foo.com, you will not be able to.
In order to do this, you need to load the page from metric.foo.com, or create the cookie under foo.com so it can be accessable under any subdomain. OR use this:
Response.cookies("mycookie").domain = ".foo.com"
...while creating it, AND before you delete it.
..untested - should work.
I had the same problem with subdomains. For some reason getting the cookie first from the request didn't work. Instead I ended up just creating a new cookie with the same cookie name, and expiry date in the past. That worked perfectly:
void DeleteSubdomainCookie(HttpResponse response, string name)
{
HttpCookie cookie = new HttpCookie(name);
cookie.Expires = DateTime.Now.AddMonths(-1);
cookie.Domain = ".yourdomain.com";
response.Cookies.Add(cookie);
}