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;
}
}
Related
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'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
I am rolling with a somewhat homebrew method of authenticating users. After authenticating the user, the authentication ticket is set like so in C#.
FormsAuthenticationTicket authenticationTicket = new FormsAuthenticationTicket(1, viewModel.Email, DateTime.Now, DateTime.Now.AddHours(48), true, String.Join("|", roles));
string encryptedTicket = FormsAuthentication.Encrypt(authenticationTicket);
HttpCookie authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
Response.Cookies.Add(authCookie);
With a small note that roles is a string list built out of the available roles to that user (The roles are not within the same user table - e.g. there is a set of conditions that define a user "role").
Next within the Application_BeginRequest method in Global.asax I have the following :
// Extract the forms authentication cookie
string cookieName = FormsAuthentication.FormsCookieName;
HttpCookie authCookie = Context.Request.Cookies[cookieName];
if (null == authCookie)
{
return;
}
FormsAuthenticationTicket authTicket = null;
try
{
authTicket = FormsAuthentication.Decrypt(authCookie.Value);
}
catch (Exception ex)
{
return;
}
if (null == authTicket)
{
return;
}
string[] roles = authTicket.UserData.Split(new char[] { '|' });
FormsIdentity id = new FormsIdentity(authTicket);
GenericPrincipal principal = new GenericPrincipal(id, roles);
HttpContext.Current.User = principal;
Basically setting the current context with the user from the authticket. However, I first ran into an issue as I was doing a custom Authorize attribute for an MVC class, and I noticed that the User of the HTTPContext was NOT set.
I then noticed that within each action, the User was not set either. I can clearly see however by stepping through my code, that the user IS being found within the authentication ticket and being decrypted OK and stored in the context variable. But by the time I get to an action within any controller, the User has vanished from the context.
EDIT :
It should also be noted that other values set on the HTTPContext do carry over to the controller. e.g. this line
HttpContext.Current.AllowAsyncDuringSyncStages = false; // Or true
Will carry whatever I set it to into the controller action. It seems to only be the User that gets blanked.
Application_BeginRequest is not a valid place to set HttpContext.Current.User, As it will be overwritten during Authorization.
You need to implement the above code in Application_AuthorizeRequest.For example refer to below code. Then it will be available in controller.
public MvcApplication()
{
this.AuthorizeRequest += MvcApplication_AuthorizeRequest;
}
void MvcApplication_AuthorizeRequest(object sender, EventArgs e)
{
FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket("test", true, 30);
FormsIdentity id = new FormsIdentity(authTicket);
GenericPrincipal principal = new GenericPrincipal(id, new string[] { });
HttpContext.Current.User = principal;
}
In my login page I creat FA cookie.
I want to add to it the userId.
I then redirect to my default page,
Where I want to read the userId.
I use this two helper methods:
public static class NewWebHelpers
{
public static void CreateAuthCookie(string cookieName, string cookieValue)
{
//Get ASP.NET to create a forms authentication cookie (based on settings in web.config)~
HttpCookie cookie = FormsAuthentication.GetAuthCookie(cookieName, false);
//Decrypt the cookie
FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(cookie.Value);
//Create a new ticket using the details from the generated cookie, but store the username &
//token passed in from the authentication method
FormsAuthenticationTicket newticket = new FormsAuthenticationTicket(
ticket.Version, ticket.Name, ticket.IssueDate, ticket.Expiration,
ticket.IsPersistent, cookieValue);
// Encrypt the ticket & store in the cookie
cookie.Value = FormsAuthentication.Encrypt(newticket);
// Update the outgoing cookies collection.
System.Web.HttpContext.Current.Response.Cookies.Set(cookie);
}
public static string ReadAuthCookie(string cookieName)
{
//Get ASP.NET to create a forms authentication cookie (based on settings in web.config)~
HttpCookie cookie = FormsAuthentication.GetAuthCookie(cookieName, false);
//Decrypt the cookie
FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(cookie.Value);
return ticket.UserData;
}
}
but get String.Empty instead of the userId I entered.
Why?
You are creating a new authcookie with FormsAuthentication.GetAuthCookie instead of reading the one which is coming with the request. Try this:
public static string ReadAuthCookie(string cookieName)
{
HttpCookie cookie =
HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(cookie.Value);
return ticket.UserData;
}
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