I have a site that uses FormsAuthentication and yes, the name of the cookie is .ASPAUX :)
I can log in perfectly. The server creates a forms authentication ticket, packs it in a cookie, properly determines the expiration time (1 year ahead) and sends it to the client.
For some reason, after some time, even though the cookie is there yet (I can see it with FireCookies) HttpContext.Current.Request.IsAuthenticated becomes false at the server. It's as if the cookie couldn't be validated.
The problem is: Why would that happen? How can I debug why the cookie suddenly becomes invalid without expiring?
EDIT
Here's the login method:
public static bool Login(int id)
{
try
{
string securityToken = UserHelper.AuthenticateUser(id);
DateTime expiryDate = DateTime.Now.AddYears(1);
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
1, id.ToString(), DateTime.Now, expiryDate, true,
securityToken, FormsAuthentication.FormsCookiePath);
string encryptedTicket = FormsAuthentication.Encrypt(ticket);
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
cookie.Expires = expiryDate;
HttpContext.Current.Response.Cookies.Add(cookie);
return true;
}
catch
{
return false;
}
}
And the web.config:
<authentication mode="Forms">
<forms loginUrl="~/Login.aspx" timeout="2880" slidingExpiration="true"/>
</authentication>
Set static machine keys in your web.config to make sure that the encryption key used in generating your ticket survives an application pool being recycled (or your website being restarted in the ASP.NET web server)?
Also see the Forms Authentication Tickets section of this MSDN library article
A few things I can think of to check:
Do you have multiple domains (including www.domain.com vs domain.com)?
If so, either set the domain in the cookie as domain.com or ensure you always use the same domain
Are you using HTTPS?
If so, make sure you're always accessing the cookie via HTTPS or making sure that Secure is set to false on the HttpCookie (otherwise it's only accessible on HTTPS requests)
Are you writing the cookie from a virtual directory?
If so, the "path" on the cookie might be set and it won't be accessible from outside the path.
Do you have multiple web servers?
If so, make sure your machine key is set to the same value (though that should be throwing an exception)
Related
I know this question has been asked before but each question has a different scenario to what I have or I am missing the obvious.
So I have implemented SSO architecture, so the user click on log in and redirected to identity server and log in there and get redirected back to the application.
in one of my application I want to use the HttpContext.Current.User.Identity.Name in my membershipprovider, but it is always empty. so I found out that I need create a cookie and set it up properly to get the HttpContext.Current.User.Identity.Name form wherever I want.
My code look like this:
After the user is redirected from the Identity server to a login control:
if (!System.Web.HttpContext.Current.User.Identity.IsAuthenticated)
{
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1,
HttpContext.Current.Session["username"].ToString(),
DateTime.Now, DateTime.Now.AddMinutes(30), true, String.Empty,
FormsAuthentication.FormsCookiePath);
string encryptedCookie = FormsAuthentication.Encrypt(ticket);
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName,
encryptedCookie);
cookie.Expires = DateTime.Now.AddMinutes(30);
Response.Cookies.Add(cookie);
FormsAuthentication.RedirectFromLoginPage("/my-profile", true);
}
My web config file looks like this:
<authentication mode="Forms">
<forms domain="http://localhost:8081" name="yourAuthCookie" loginUrl="login.aspx"
protection="All" path="/" />
</authentication>
<authorization>
<allow users="?" />
</authorization>
after the user is redirected to the my profile page the System.Web.HttpContext.Current.User.Identity.IsAuthenticated is true and the AuthinticationType = Cookies. but the Name is empty.
I am using asp.net webforms.
UPDATE: I have tried to set the HttpContext.Current.User it is set properly but when the page load or redirected it goes back to null empty.
if (ticket != null && !ticket.Expired)
{
var roles = ticket.UserData.Split(',');
System.Web.HttpContext.Current.User = new System.Security.Principal.GenericPrincipal(new FormsIdentity(ticket), roles);
}
any help will be much appreciated.
Your domain is wrong - in your app.config; <forms domain="http://localhost:8081" ... /> in this context domain has a meaning of domain into which the cookie is set. You can read more about domain of cookies here.
Domain is something like www.google.com or .google.com etc - it doesn't contain protocol prefix, it doesn't contain port and it makes no sense to use it for local hosts - so drop that attribute from your web.config for good.
Also (and this has nothing to do with your issue, it's just a friendly suggestion) - you don't have to construct forms auth ticket when all you care about is username. You can use just FormsAuthentication.SetAuthCookie instead.
I'm trying to get a subdomain to create a cookie for the entire domain instead of just its subdomian using the property cookie.domain, so the two subdomains can share the cookie info. When I deploy to IIS I get 2 cookies created, one for the domain and another for the subdomain. Why is that? When I update the cookie the only one that gets updated is the subdomain one, rendering the domain cookie kind of useless.
I tried to trace this through my code, but running visual studio in debug mode doesn't actually set any cookie at all, unless I don't set the cookie.domain property. Leaving domain.cookie out I get a cookie set to localhost, but only that one cookie. Any thoughts?
Okay, so I finally answered my own quesion(s). I'll tackle the second one first, about running the site in debug mode:
Visual studio debugs to the site http://localhost:[someport]. So if the code is set to create a cookie using cookie.domain for mydomain.com, the cookie isn't set because the browser knows you're at localhost instead of the domain specified. To remedy this I put an entry in my hosts file so that mydomain.com is pointed to 127.0.0.1. Then I fired up the site in debug mode. When the site came up as localhost I changed the URL in the browswer to http://subdomain.domain.com:[someport] and refreshed. Now the cookie can be set.
Doing this helped me trace through my code to find the issue of two cookies being created by my subdomain website. What I found is that the mydomain.com cookie was being created okay (CreateCookie method below) because of cookie.domain. However, when I was trying to update the expiration on the cookie (UpdateCookie below) it reverted back to thinking it should be using the subdomain cookie and went ahead and created it when it didn't find one. All I had to do was set cookie.domain again before setting the cookie and updating the expiration. Now I only have one cookie.
public void CreateCookie()
{
HttpCookie cookie = new HttpCookie(mConfig.webCookie);
TimeSpan span = new TimeSpan(0, 0, 30, 0);
DateTime time = DateTime.Now; ;
cookie["Username"] = mEncrypt.Encrypt(mUser.Username);
cookie.Domain = "mydomian.com";
cookie.Expires = time + span;
HttpContext.Current.Response.Cookies.Add(cookie);
}
public void UpdateCookie()
{
TimeSpan span = new TimeSpan(0, 0, 30, 0);
DateTime time = DateTime.Now;
HttpCookie cookie = HttpContext.Current.Request.Cookies[mConfig.webCookie];
// without specifying the domain the cookie will be set with the subdomain
cookie.Domain = "mydomain.com";
HttpContext.Current.Response.Cookies.Set(cookie);
HttpContext.Current.Response.Cookies[mConfig.webCookie].Expires = time + span;
}
You can set this cookie name for the full domain and subdomain on web.config on httpCookies
<httpCookies domain="domain.com" httpOnlyCookies="false" requireSSL="false" />
set domain.com and NOT www.domain.com to archive what you say, to have the same cookie on domain and sub domain. Similar there is a parameter on authentication that you set this cookie settings, depend for what cookie you talking about.
In your question "why is that?" the answer is that if you not set this parameters for the cookies then the cookies actually use the current host name, so they are different if the first name of the sub-domain change.
In my asp.net web application, I'm using asp.net forms authentication with following configuration.
<authentication mode="Forms">
<forms name=".ASPNETAUTH" loginUrl="Login.aspx" protection="None" timeout="20" />
</authentication>
After form authentication time out, I would like to redirect to a different page. For example to 'SessionTimedOut.aspx' page.
I've found other questions on here, here is one, Forms Authentication Timeout vs Session Timeout
The answer given makes sense but the first line of code has me confused.
var cookie = Retrieve AuthenticationCookie();
if (cookie == null) return;
FormsAuthenticationTicket ticket = null;
try {
ticket = FormsAuthentication.Decrypt(cookie.Value);
} catch (Exceptoin decryptError) {
// Handle properly
}
if (ticket == null) return; // Not authorised
if (ticket.Expiration > DateTime.Now) {
Response.Redirect("SessionExpiredPage.aspx"); // Or do other stuff here
}
Now there is a
FormsAuthentication.GetAuthCookie()
which takes a username and bool to persist the cookie, but this is for creating an auth cookie not getting it. So, what would the var cookie, first line of code look like.
At the moment, I am using " in web config and then when user logins in settings a session and then on every post back in a page init in my base page am checking if that session is null, if so, redirecting to a session timed out page. This is not really what I want.
May have found out how to get cookie,
HttpCookie cookie = Context.Request.Cookies[FormsAuthentication.FormsCookieName];
FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(cookie.Value);
This doesn't work because when the authentication ticket expires, the cookie goes away and the the cookie var is null. Any other way to get this working? I would still like on post back check if authentication has expired and then take appropriate action. Any thoughts from anyone????
The thing to remember is that even though your session times out on the server end, the client end will not process anything until it's next request. At that time it will discover that it's session has expired and attempt to restart the session. A Response.Redirect or even Server.Redirect call won't help with this.
What you need to do is to synchronize your server timeout with your client timeout, and have some client script in place to redirect the user to a "Timed Out" type page. I've written up an article with some sample code on how to do that here.
Q: So, what would the var cookie, first line of code look like.?
var cookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName]
I have a login problem.
First i am using SSL while logging.
When i log in, i am creating a cookie like this. when i check if it is secure the answer is yes.
FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(1, // version
UserName.Text, // user name
DateTime.Now, // creation
DateTime.Now.AddMinutes(60),// Expiration
false, // Persistent
role); // User data
// Now encrypt the ticket.
string encryptedTicket = FormsAuthentication.Encrypt(authTicket);
// Create a cookie and add the encrypted ticket to the
// cookie as data.
HttpCookie authCookie =
new HttpCookie(FormsAuthentication.FormsCookieName,
encryptedTicket);
if (authCookie.Secure)
{
new GUIUtility().LogMessageToFile("The cookie is secure with SSL.");
// Add other required code here.
}
authCookie.Secure = FormsAuthentication.RequireSSL;
// Add the cookie to the outgoing cookies collection.
HttpContext.Current.Response.Cookies.Add(authCookie);
// Redirect the user to the originally requested page
Response.Redirect(FormsAuthentication.GetRedirectUrl(UserName.Text,false));
then this is redirected to the global.asax page which has this code:
string cookieName = FormsAuthentication.FormsCookieName.ToString();
HttpCookie authCookie = Context.Request.Cookies[cookieName];
try
{
new GUIUtility().LogMessageToFile(cookieName + authCookie.Secure);
}
catch (Exception)
{
//
}
here i get the cookieName as ".ASPXAUTH" and authCookie.Secure value as False.
Why is this happening i want the authCookie.Secure value to be true here.
Any suggestions?? thanks
my web config has this:
<authentication mode="Forms">
<forms loginUrl="Login.aspx" defaultUrl="~/Default.aspx" slidingExpiration="true" timeout="120" path="/" requireSSL="true" protection="All">
</forms>
</authentication>
<httpCookies requireSSL="true"/>
<authorization>
<deny users="?"/>
<!--<allow users="*"/>-->
</authorization>
Restrict the Authentication Cookie-to-HTTPS Connections
Cookies support a "secure" property that determines whether or not browsers should send the cookie back to the server. With the secure property set, the cookie is sent by the browser only to a secure page that is requested using an HTTPS URL.
If you are using .NET Framework version 1.1, set the secure property by using requireSSL="true" on the element as follows:
<forms loginUrl="Secure\Login.aspx"
requireSSL="true" . . . />
If you are using .NET Framework version 1.0, set the secure property manually in the Application_EndRequest event handler in Global.asax using the following code:
protected void Application_EndRequest(Object sender, EventArgs e)
{
string authCookie = FormsAuthentication.FormsCookieName;
foreach (string sCookie in Response.Cookies)
{
if (sCookie.Equals(authCookie))
{
// Set the cookie to be secure. Browsers will send the cookie
// only to pages requested with https
Response.Cookies[sCookie].Secure = true;
}
}
}
so according to me the first option is not working in web config so im doing it manually
which is the second option in the code..
Please suggest.
Are you redirecting on log-in to a non-SSL resource? If this is the case, then the cookie you created in the first piece of code shouldn't be used, because it's a secure cookie and hence only applicable to SSL connections (i.e. you explicitly said it shouldn't be sent to non-SSL requests, that's what .Secure does), and hence a new cookie would be created. I would expect it to also not include the ticket value.
In this case, you're going to want to either:
Keep with SSL from the point of being logged in.
Live with the risk of session stealing (there are further means of mitigating this risk).
Use an authentication protocol like Digest or NTLM that allows for challenge-response and for you to more rapidly expire the log-in without the user being pestered (because the browser does the second log-in for you).
I want to have functionality on my application that lets a user check off that they wish to stay logged indefinitely (arbitrarily setting the cookie expiration at 3 months from NOW).
The code I have for dealing with this is
private static HttpCookie GetFormsAuthenticationCookie(string userNameResponse,
bool persistCookie)
{
var cookie = FormsAuthentication.GetAuthCookie(userNameResponse, persistCookie);
if (persistCookie)
cookie.Expires = DateTime.Now.AddMonths(3);
return cookie;
}
private void LoginUser(string userNameResponse, bool PersistCookie)
{
Response.Cookies.Add(GetFormsAuthenticationCookie(userNameResponse, PersistCookie));
string navigateAfterUrl = FormsAuthentication.GetRedirectUrl(userNameResponse,
PersistCookie);
Response.Redirect(navigateAfterUrl);
}
However at some point later when I return to the site I need to login again. I have verified that the cookie comes back with my expiration date and that it is not set as a session cookie (also tested with closing/reopening browser and cookie still exists). My one thought is that it has something to do with when ASP.NET expires the session.
I have a specific machine key setup in my web.config so shouldn't the same cookie work if IIS gets restarted etc? Does anyone have any suggestions on what could either be causing this or atleast on how to trace this further since I can't think of anything else to do.
When you call the GetAuthCookie method a FormsAuthenticationTicket is created with a timeout given by the Timeout property in web.config. So be sure to set it properly:
<authentication mode="Forms">
<forms
loginUrl="/someloginUrl"
requireSSL="true"
protection="All"
// This is the setting you are looking for! (it's in seconds)
timeout="120"
domain="example.com"
slidingExpiration="false"
name="cookieName" />
</authentication>
Once the ticket is encrypted it is used as a value for the cookie. When you set the Expires property of your cookie to a given value this indicates that it will be persisted on the client computer for the given period. Then on every request ASP.NET runtime will check the presence of the cookie, will try to decrypt the value and obtain the ticket. Next it will check if the ticket is still valid by using the Timeout property, so if you have a small timeout, no matter that your cookie is still transmitted, the ticket is no longer valid and the authentication will fail.