I'm writing an ASP.net MVC 5 application using FormsAuthentication. I had everything up and working properly using FormsAuthentication.SetAuthCookie(user.Email, model.RememberMe).
However, I wanted to create a custom ticket so I could store some extra information in the UserData field of the ticket. This is how I'm creating my ticket and storing it in a cookie:
var ticket = new FormsAuthenticationTicket(1, user.Email, DateTime.Now, DateTime.Now.AddMinutes(FormsAuthentication.Timeout.Minutes), model.RememberMe, user.AuthToken);
var encryptedTicket = FormsAuthentication.Encrypt(ticket);
var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket) { Domain = FormsAuthentication.CookieDomain, Path = FormsAuthentication.FormsCookiePath, HttpOnly = true, Secure = FormsAuthentication.RequireSSL };
HttpContext.Response.Cookies.Add(cookie);
This creates an encrypted ticket and sends it to the browser. I've verified with developer tools and Fiddler that the ticket is present in the browser and that it is sent back to the server on the subsequent requests.
But authentication is now broken. Also, the cookie is not available in Application_AuthenticateRequest or Application_PostAuthenticateRequest events. When I use the debugger to explore Context.Request.Cookies it is not present in the list.
Oddly enough the cookie does exist if I step back in the pipeline and check it in Application_BeginRequest:
void Application_BeginRequest(object sender, EventArgs e)
{
// Auth cookie exists in the collection here! Ticket decrypts successfully
HttpCookie authCookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie == null)
return;
var encTicket = authCookie.Value;
var ticket = FormsAuthentication.Decrypt(encTicket);
}
void Application_AuthenticateRequest(object sender, EventArgs e)
{
// Auth cookie missing from the cookies collection here!
HttpCookie authCookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie == null)
return;
var encTicket = authCookie.Value;
var ticket = FormsAuthentication.Decrypt(encTicket);
using (var db = new BadgerContext())
{
var user = db.Users.OfType<RegisteredUser>().FirstOrDefault(x => x.UserName == ticket.Name);
if (ticket.UserData != user.AuthToken)
{
FormsAuthentication.SignOut();
Response.Redirect(FormsAuthentication.DefaultUrl);
}
}
}
So it appears that something is stripping my custom FormsAuthenticationTicket out of the cookies after BeginRequest but before AuthenticateRequest. Unfortunately, this breaks authentication altogether on the site.
Any ideas what is causing this behavior when I create a custom ticket? Am I doing something wrong with my cookie creation?
Check in the .config file the inside the system.web node, the httpRuntime tag.
<httpRuntime targetFramework="4.5" />
as same as main web site
Rowan suggested I look at the value for FormsAuthentication.Timeout.Minutes. After investigation, this value always came back as 0. This led to an immediate expiration of the ticket. I had to use FormsAuthentication.Timeout.TotalMinutes instead and everything started working properly
Related
I am currently trying to replace our company wide user authentication that we use for all our internal web apps and what not as our current one was made in 2006 and fails on the regular. I was told to make it as simple as possible to implement on all existing projects. It is a .NET class library. It's .dll will be added as a reference to existing projects.
I am having an issue where I can log in exactly one time after all cookies have been cleared. Once I logout and log back in I get System.ArgumentException: Invalid value for 'encryptedTicket' parameter. I found some posts suggesting the cookie may be null, or I'm not trying to decrypt the name and not the value, but that wasn't the case. This happens on chrome and edge.
The user is authenticated every time though, assuming the correct username and password is used as I get redirected to the success page.
After authentication I add a cookie and then redirect.
private void AddCookie(int compID, bool persist, HttpContext httpContext)
{
httpContext.Request.Cookies.Add(SetUpSession(compID, persist));
FormsAuthentication.RedirectFromLoginPage(compID.ToString(), persist);
}
My method for creating the cookie
private HttpCookie SetUpSession(int companyID, bool persist)
{
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
1, // ticket version
companyID.ToString(), // authenticated username
DateTime.Now, // issueDate
DateTime.Now.AddMinutes(30), // expiryDate
persist, // true to persist across browser sessions
FormsAuthentication.FormsCookiePath); // the path for the cookie
String encTick = FormsAuthentication.Encrypt(ticket);
HttpCookie cookie = new HttpCookie("Cookie", encTick);
cookie.HttpOnly = true;
return cookie;
}
After I redirect to the success page there is a snipped of code that checks to see if the user is logged in. This is where the error happens
public dynamic isLoggedIn(HttpContext httpContext)
{
AuthenticationUtilities authUtil = new AuthenticationUtilities();
if (httpContext.Response.Cookies["Cookie"] != null)
{
companyID = authUtil.Authenticate(httpContext.Request.Cookies["Cookie"]);//the error occurs here
authUtil = new AuthenticationUtilities(companyID);
return authUtil;
}
else
{
httpContext.Response.Redirect("~/login.aspx");
return null;
}
}
The method that decrypts the cookie
public int Authenticate(HttpCookie cookie)
{
FormsAuthenticationTicket authTick = FormsAuthentication.Decrypt(cookie.Value);
return int.Parse(authTick.Name);
}
this method is called on any page that requires the user to be logged in, like this.
LMFJAuth.AuthenticationUtilities auth = _LMFJAuth.isLoggedIn(HttpContext.Current);//if the cookie is null it redirects to login.
This is the logout method
public void LogOut(HttpContext httpContext)
{
FormsAuthentication.SignOut();
HttpCookie cookie = new HttpCookie("Cookie");
cookie.Expires = DateTime.Now.AddMinutes(-1);
httpContext.Session.Clear();
httpContext.Response.Cookies.Add(cookie);
httpContext.Response.Redirect(FormsAuthentication.LoginUrl);
}
Can somone help explain what may be going on in which the value for the encrypted ticked is coming up as invalid after the first successful login/logout?
For me it was that the encrypted value of cookie.Value was coming up as greater than the maximum value of 4096, being 4200 in my case. I had just added some role strings to the user data.
I found it help to look up the source code of Microsoft classes when I'm stuck, in this case I used:
http://www.dotnetframework.org/default.aspx/DotNET/DotNET/8#0/untmp/whidbey/REDBITS/ndp/fx/src/xsp/System/Web/Security/FormsAuthentication#cs/1/FormsAuthentication#cs.
My web app based on.NET/Webforms does not work like expected because of losing value of set cookie after redirecting to page on the same app domain. But this app worked perfect before. The problem is as following:
Set cookie on Login:
protected void LogIn(object sender, EventArgs e)
{
if (IsValid)
{
. . .
string userData = JsonConvert.SerializeObject(eng);
FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(
1,
UserName.Text,
DateTime.Now,
DateTime.Now.AddMinutes(15),
false,
userData);
string encTicket = FormsAuthentication.Encrypt(authTicket);
HttpCookie faCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);
Response.Cookies.Add(faCookie);
Response.Redirect("~/View_Id");
}
}
else
{
FailureText.Text = "Invalid username or password.";
ErrorMessage.Visible = true;
}
}
Setting a new cookie works fine, but the value get lost after redirecting. The value will be need on Global.FormsAuthentication_OnAuthenticate(Object sender, FormsAuthenticationEventArgs e).
ok fox, after reading about cookie i found out, that the set user data was too big (more than allowed 4k) - hence the set cookie value will be lost or overrided with null. Reducing the size of user data to the max. limit of 4k is the solution!
I have ported solution from vs2013 to 2015. My forms authentication is not working anymore.
On the first request I make user login and create cookie.
If I refresh page or click anywhere my ASPXAUTH cookie is not present anymore.
In chrome I can see that I have response cookie with the same name. Not sure why.
Request Cookies 613
.ASPXAUTH C83F564B3B0C929EA69218DC83C651DB2376C2541E9F83C4CD6FCB6B76570B68DD0DF1BB184531ACDABAA2F565C274D59B9958D372C4B927E2D34CD6D1337E26D82D346729DB2B60B263D51FF6BC1E5B464FDF5B32E003F9C963A7A5815D415C113C7A038D46FFD1441FE1F97F604CD1595BF4A430071BB4C47C8E18C4730E18BC827388 N/A N/A N/A 274
ASP.NET_SessionId 2pyvx1ia0rlsfymsmormbbcg N/A N/A N/A 44
Response Cookies 18 .ASPXAUTH / Session 18
The code I'm using to create auth cookie is:
Dim ticket As FormsAuthenticationTicket = New FormsAuthenticationTicket(1, userName, Date.Now, Date.Now.AddMinutes(timeout), True, String.Empty)
Dim encTicket As String = FormsAuthentication.Encrypt(ticket)
Dim loginCookie As HttpCookie = New HttpCookie(FormsAuthentication.FormsCookieName)
loginCookie.Value = encTicket
Response.Cookies.Add(loginCookie)
Is there any explanation to this behaviour? Thanks
In your code, you create FormsAuthenticationTicket manually.
In addition to the above code, you also need to retrieve the username from cookie on every request, and save it in IPrincipal Object, so that you can access User.Identity.
Global.asax.cs
public class Global : HttpApplication
{
private void Application_AuthenticateRequest(object sender, EventArgs e)
{
HttpCookie decryptedCookie =
Context.Request.Cookies[FormsAuthentication.FormsCookieName];
FormsAuthenticationTicket ticket =
FormsAuthentication.Decrypt(decryptedCookie.Value);
var identity = new GenericIdentity(ticket.Name);
var principal = new GenericPrincipal(identity, null);
HttpContext.Current.User = principal;
Thread.CurrentPrincipal = HttpContext.Current.User;
}
}
I've been trying to figure out how to set the secure flag on all the server cookies for our website. We're running .NET 4.5. I tried adding <httpCookies requireSSL="true" /> to the web.config file. I tried adding <authentication><forms requireSSL="true" /></authentication>. I tried setting the secure flag in code. Nothing had any effect. Adding the following c# function to Global.asax.cs was supposed to work, but didn't:
protected void Application_EndRequest()
{
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
var httpCookie = Response.Cookies[sCookie];
if (httpCookie != null) httpCookie.Secure = true;
}
}
It finally started working after I got rid of the "if (sCookie.Equals(authCookie))..." statement. So this is the working version:
protected void Application_EndRequest()
{
string authCookie = FormsAuthentication.FormsCookieName;
foreach (string sCookie in Response.Cookies)
{
// Set the cookie to be secure. Browsers will send the cookie
// only to pages requested with https
var httpCookie = Response.Cookies[sCookie];
if (httpCookie != null) httpCookie.Secure = true;
}
}
I have several questions. First, what is the logic behind putting this in the Application_EndRequest method? Second, why did I have to get rid of the sCookie.Equals(authCookie)) part? Finally, has anyone found a more elegant solution? Thanks.
If you are executing the request over HTTP and not HTTPS then I do not think you can set Secure = true. Can you verify that you are running over a secure connection? You can do some google / bing searches on how to generate a local certificate if you are testing on your dev box. Also do not forget to encrypt your cookie so its not readable on the client side.
Here is some sample code.
var userName = "userName";
var expiration = DateTime.Now.AddHours(3);
var rememberMe = true;
var ticketValueAsString = generateAdditionalTicketInfo(); // get additional data to include in the ticket
var ticket = new FormsAuthenticationTicket(1, userName, DateTime.Now, expiration, rememberMe, ticketValueAsString);
var encryptedTicket = FormsAuthentication.Encrypt(ticket); // encrypt the ticket
var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket)
{
HttpOnly = true,
Secure = true,
};
EDIT - Added link
Also take a look at this previous answer and how you can configure your web.config to ensure that cookies are always marked as secure.
I am building an application in ASP.NET MVC 3 in which I have implemented Forms authentication.
I would also like to give the user the option to Sign-up/log-in with their facebook account(and possibly other social accounts in the future.)
I am using the C# Facebook SDK
I have successfully implemented the facebook login workflow. Now my question is, how to handle mixing both Forms and Facebook Auth? I can't seem to find any sufficient examples of how to accomplish this.
In regards to my facebook implementation, I am requesting permission from the user for a non-expiring Auth Token which I will store in my database.
For this you will have to do something like this
Create a custom class (CurrentIdentity) which implements IIdentity. Override .ToString() for this class, and so that you have some sort of serialized state of object. I had use "|" seperator. This string will be stored in encrypted cookie.
Create a session cookie
public static HttpCookie AuthCookie(CurrentIdentity identity) {
//Create the Authticket, store it in Cookie and redirect user back
FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(1,
identity.Name, DateTime.Now, DateTime.Now.AddHours(3), true, identity.ToString(), FormsAuthentication.FormsCookiePath);
string encryptedTicket = FormsAuthentication.Encrypt(authTicket);
HttpCookie authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
authCookie.Expires = authTicket.Expiration;
return authCookie;
}
Add this cookie to response.
HttpResponse res = HttpContext.Current.Response;
res.Cookies.Add(Helper.AuthCookie(identity));
res.Redirect(FormsAuthentication.GetRedirectUrl(identity.Name, true));
res.End();
Now in Gloabl.asax, inside *Application_AuthenticateRequest* read this cookie and populate your CurrentIdentity object, something like this.
if (Request.IsAuthenticated == true) {
// Extract the forms authentication cookie
string cookieName = FormsAuthentication.FormsCookieName;
HttpCookie authCookie = Context.Request.Cookies[cookieName];
if (null == authCookie) {
// There is no authentication cookie.
return;
}
FormsAuthenticationTicket authTicket = null;
try {
authTicket = FormsAuthentication.Decrypt(authCookie.Value);
} catch (Exception) {
// Log exception details (omitted for simplicity)
return;
}
if (null == authTicket) {
// Cookie failed to decrypt.
return;
}
// When the ticket was created, the UserData property was assigned a
// pipe delimited string of role names.
string[] userInfo = authTicket.UserData.Split('|');
// Create an Identity object
FormsIdentity id = new FormsIdentity(authTicket);
//Populate CurrentIdentity, from Serialized string
CurrentIdentity currentIdentity = new CurrentIdentity(userInfo[0], userInfo[1], userInfo[2]);
System.Security.Principal.GenericPrincipal principal = new System.Security.Principal.GenericPrincipal(currentIdentity, userInfo);
Context.User = principal;
}
This should solve your problem. I have implemented similar thing on my company's website.
merge facebook authentication with your forms authentication.
When user login using forms auth you create FormsAuthenticationTicket, when login from facebook also create FormsAuthenticationTicket