Banging my head against a brick wall again. I'm trying to get my ASP.NET Web Forms web app to log out but it's refusing to do so. I'm using Forms Authentication. The problem seems to be that the browser (ALL of the ones I've tried) are maintaining a cache of the pages after logging in but not clearing that cache on logout.
When I click the logout link on the main page, it transfers me successfully to the login page but I can just type in the page's URL or press back on the browser and it loads up the page again without needing to log in.
I've spent the past couple of hours scouring StackOverflow and elsewhere for a solution but so far nothing has worked.
My root web.config has this:
<authentication mode ="Forms">
<forms loginUrl="~/Account/Login" name=".ASPXFORMSAUTH" defaultUrl="~/Default.aspx"></forms>
</authentication>
<authorization>
<deny users="?"/>
</authorization>
<compilation debug="true" targetFramework="4.5"/>
<httpRuntime targetFramework="4.5"/>
This is the web.config in my Account folder.
<configuration>
<location path="Manage.aspx">
<system.web>
<authorization>
<allow users="*"/>
</authorization>
</system.web>
</location>
</configuration>
This is my code for logging out. As you can see, I've implemented everything I've found online. This is in my master page's cs file.
public void Logout_Click(object sender, EventArgs e)
{
ClearSession();
// Clear authentication cookie
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, "");
cookie.HttpOnly = true;
cookie.Expires = DateTime.Now.AddYears(-1);
Response.Cookies.Add(cookie);
// Clear session cookie
SessionStateSection sessionStateSection = (SessionStateSection)WebConfigurationManager.GetSection("system.web/sessionState");
HttpCookie cookie2 = new HttpCookie(sessionStateSection.CookieName, "");
cookie2.Expires = DateTime.Now.AddYears(-1);
Response.Cookies.Add(cookie2);
FormsAuthentication.RedirectToLoginPage();
}
protected void ClearSession()
{
FormsAuthentication.SignOut();
Session.Clear();
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.ExpiresAbsolute = DateTime.UtcNow.AddDays(-1d);
Response.Expires = -1500;
Response.CacheControl = "no-Cache";
}
In my Page_Init (again, master page cs file), I have this:
protected void Page_Init(object sender, EventArgs e)
{
HttpContext.Current.Response.AddHeader("Cache-Control", "no-cache, no-store, must-revalidate");
HttpContext.Current.Response.AddHeader("Pragma", "no-cache");
HttpContext.Current.Response.AddHeader("Expires", "0");
// The code below helps to protect against XSRF attacks
var requestCookie = Request.Cookies[AntiXsrfTokenKey];
Guid requestCookieGuidValue;
if (requestCookie != null && Guid.TryParse(requestCookie.Value, out requestCookieGuidValue))
{
// Use the Anti-XSRF token from the cookie
_antiXsrfTokenValue = requestCookie.Value;
Page.ViewStateUserKey = _antiXsrfTokenValue;
}
else
{
// Generate a new Anti-XSRF token and save to the cookie
_antiXsrfTokenValue = Guid.NewGuid().ToString("N");
Page.ViewStateUserKey = _antiXsrfTokenValue;
var responseCookie = new HttpCookie(AntiXsrfTokenKey)
{
HttpOnly = true,
Value = _antiXsrfTokenValue
};
if (FormsAuthentication.RequireSSL && Request.IsSecureConnection)
{
responseCookie.Secure = true;
}
Response.Cookies.Set(responseCookie);
}
Page.PreLoad += master_Page_PreLoad;
}
And finally in my master page header, I have these meta tags.
<meta http-equiv="cache-control" content="no-cache" />
<meta http-equiv="Expires" content="0" />
<meta http-equiv="cache-control" content="no-store" />
<meta http-equiv="cache-control" content="must-revalidate" />
<meta http-equiv="cache-control" content="proxy-revalidate" />
As is usually the case, I figure out the problem after doing some more searching and hitting a lightbulb moment - right after requesting help.
In case someone else is having problems, here's the solution that worked for me.
Although it's set up to use Forms Authentication, when I looked back at the default asp.net login page and the associated cs page I realised it was using the Identity.Owin namespace for logging in.
So in my logout, I replaced the first block of code above with:
public void Logout_Click(object sender, EventArgs e)
{
ClearSession();
FormsAuthentication.RedirectToLoginPage();
}
protected void ClearSession()
{
Context.GetOwinContext().Authentication.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
Session.Abandon();
}
Related
Good afternoon.
I have a situation with a SSL web application written in C# in that if I log in and navigate to a secure page, close my browser and open it again after the login has expired, the content that was shown when closing the browser still appears.
In other words, if the login cookie expires after 30 minutes, and I close the browser and re-open it (restoring my tabs) after 30 minutes, the page that I was viewing still appears and not the expected login page.
If I click on a link after re-opening the browser that is supposed to take me to another secure location, I am redirected to the login page, but I'd like to prevent the previous page content from being shown again since it could contain sensitive data.
The page content is set to expire after 30 minutes and the user login cookie is set to expire after 30 minutes as well. (See images below)
I'm not sure how to force the login screen to appear when re-opening a browser. Thoughts or help would be greatly appreciated!
EDIT: Relevant content of web.config if it will help.:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.web>
[REMOVED]
<authentication mode="Forms">
<forms path="/" cookieless="UseCookies" loginUrl="~/Account/Login.aspx" name="[REMOVED]" requireSSL="true" timeout="30" />
</authentication>
[REMOVED]
</system.web>
<system.webServer>
<staticContent>
<clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="00:30:00" />
</staticContent>
[REMOVED]
</system.webServer>
[REMOVED]
</configuration>
OnLoad Method from the Site.Master page:
protected void Page_Load(object sender, EventArgs e)
{
/*
* See if the user has read the service agreement, if not, send the user to the service agreement page.
* Do NOT process this if it is being called from the service agreement page!
* If you do, we will end up with an infinite loop and infinite loops are bad mmmkay?
*/
if (!Profile.IsAnonymous && !Roles.IsUserInRole("Administrator"))
{
if (!Profile.ServiceAgreementStatusOkay())
{
if (Page.Request.RawUrl != #"/Account/KB/SA/")
{
Response.Redirect("/Account/KB/SA/");
}
}
}
// only check messages if the user isn't anonymous
if (!Profile.IsAnonymous)
{
int UnreadMessageCount = 0;
SqlDataSourceMessageCount.SelectParameters["UserId"].DefaultValue = Membership.GetUser().ProviderUserKey.ToString();
SqlDataSourceMessageList.SelectParameters["UserId"].DefaultValue = Membership.GetUser().ProviderUserKey.ToString();
Repeater MessageListRepeater = ((Repeater)LoginViewUserMenu.FindControl("RepeaterMessages"));
SqlDataReader CountReader = null;
DataView MessageListDataView = new DataView();
try
{
CountReader = (SqlDataReader)SqlDataSourceMessageCount.Select(DataSourceSelectArguments.Empty);
if (CountReader.HasRows)
{
CountReader.Read();
UnreadMessageCount = CountReader.SafeGetInt32(0);
if (UnreadMessageCount != 0)
{
((Literal)LoginViewUserMenu.FindControl("LiteralMessageCount")).Text = UnreadMessageCount.ToString();
}
else
{
((Literal)LoginViewUserMenu.FindControl("LiteralMessageCount")).Text = string.Empty;
}
}
SqlDataSourceMessageList.FilterParameters.Clear();
SqlDataSourceMessageList.FilterExpression = string.Empty;
SqlDataSourceMessageList.FilterParameters.Add("IsRead", "false");
SqlDataSourceMessageList.FilterExpression = "IsRead = {0}";
MessageListDataView = (DataView)SqlDataSourceMessageList.Select(DataSourceSelectArguments.Empty);
MessageListRepeater.DataSource = MessageListDataView;
MessageListRepeater.DataBind();
}
catch (Exception ex)
{
ErrorNotifier Err = new ErrorNotifier();
Err.Notify(ex, HttpContext.Current.Request.Url.AbsoluteUri.ToString());
throw;
}
finally
{
if (CountReader != null)
{
if (!CountReader.IsClosed)
{
CountReader.Close();
}
}
}
}
// set the value of the bug report path.
TextBoxPath.Text = Page.Request.RawUrl;
// set footer stuff.
AppCopyrightDate.Text = AppAttributes.GetCopyrightString();
LiteralVersionNumber.Text = AppAttributes.AppVersion.ToString();
// set theme info.
ThemeManager TM = new ThemeManager();
if (Request["ThemePreview"] == null)
{
LiteralTheme.Text = TM.GetThemeLink(Profile.UX.Theme);
}
else
{
LiteralTheme.Text = TM.GetThemeLink(Request["ThemePreview"]);
}
}
I want to redirect user based upon role.I can do it without using Forms Authentication but I want to do it with forms authentication. Following is my code:
Web.Config
<authentication mode="Forms">
<forms loginUrl="Forms/Login.aspx" defaultUrl="Member/Home.aspx">
</forms>
</authentication>
<authorization>
<deny users="?"/>
</authorization>
Login.aspx.cs
protected void btnLogin_Click(object sender, EventArgs e)
{
members.memberEmail = txtEmail.Text;
members.memberPassword = operation.EncodePasswordToBase64(txtPassword.Text);
DataSet ds = operation.GetUsers(members);
if (ds != null)
{
int role = int.Parse(ds.Tables[0].Rows[0]["memberType"].ToString());
if (role == 2)
{
Response.Redirect("../Member/Home.aspx");
}
else if(role == 1)
{
Response.Redirect("../Admin/Home.aspx");
}
}
}
Here GetUsers function giving back the Dataset of members and I am checking role from DataSet and redirecting the user to respective home page. I am trying to accomplish same thing using forms authentication:
I have enabled the role manager in web config:
<roleManager enabled="true">
</roleManager>
I know, I am doing wrong. Can anyone guide me?
I'm trying to create a simple setup where the user navigates to a page and if they're not authenticated, taken to a login page. If the user enters the correct username and password, he/she is taken back to the first page where they have the ability to add data to a SQL database. Unfortunately as of now, when the user is authenticated, they get bounced from the first page back to the login page (e.g. the user isn't being authenticated). Here's what I have for code:
The applicable code in first page (where the user can enter data)
protected void Page_Load(object sender, EventArgs e)
{
/*Make sure the user is authenticated. If not, redirect them to the Login page*/
if (!HttpContext.Current.User.Identity.IsAuthenticated)
FormsAuthentication.RedirectToLoginPage();
else
LabelMsg.Text = "Authenticated: " + HttpContext.Current.User.Identity.IsAuthenticated.ToString();
}//end Page_Load()
The applicable code in login page:
using (SqlDataAdapter sda = new SqlDataAdapter())
{
sda.SelectCommand = myCommand;
DataTable dt = new DataTable();
sda.Fill(dt);
GridView GridViewBookList = new GridView();
GridViewBookList.DataSource = dt;
GridViewBookList.DataBind();
if (GridViewBookList.Rows.Count > 0)
{
FormsAuthentication.SetAuthCookie("admin", true);
FormsAuthentication.RedirectFromLoginPage("admin", true);
}
else
LabelMsg.Text = "Incorrect username or password";
}
Web.Config piece
<location path="~/whatsnew/add-newbook.aspx">
<!--Unauthenticated users cannot access this page-->
<system.web>
<authentication mode="Forms">
<!--.NEWBOOK is the name of the cookie used in authorization-->
<forms loginUrl="~/whatsnew/login.aspx" defaultUrl="~/default.aspx" requireSSL="true" name=".NEWBOOK"/>
</authentication>
<authorization>
<deny users="?"/>
<allow roles="admin"/>
</authorization>
</system.web>
Any help would be greatly appreciated.
I think you need to check Request.IsAuthenticated in your code. If you would like to use the HttpContext.Current.User.IsAuthenticated, in my experience I have had to set it by saying something like the following in my login page:
string username = "My username";
string[] roles = new string[] {"Role1", "Role2"};
HttpContext.Current.User =
new GenericPrincipal(new GenericIdentity(userName), roles);
I can see you're doing simple forms authentication. Have you tried adding WebSecurity.Login before you set the cookie?
WebSecurity.Login("admin", pwd, True);
FormsAuthentication.SetAuthCookie("admin", true);
FormsAuthentication.RedirectFromLoginPage("admin", true);
I'm trying to create a basic role based user access via FormsAuthenticationTicket but it's not working correctly as it doesn't seem to be passing the role to the page. The code I'm using is:
web.config:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<location path="HRPages">
<system.web>
<authorization>
<allow roles = "HR" />
<deny users ="*" />
</authorization>
</system.web>
</location>
<location path="SalesPages">
<system.web>
<authorization>
<allow roles = "Sales" />
<deny users ="*" />
</authorization>
</system.web>
</location>
<system.web>
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5" />
<authentication mode="Forms" />
</system.web>
</configuration>
Login Page:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.Security;
namespace formlogin
{
public partial class Login : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void cmdLogin_Click(object sender, EventArgs e)
{
if (this.txtUsersname.Text.Trim() == "1"
&& this.txtPassword.Text.Trim() == "2")
{
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
1, // Ticket version
this.txtUsersname.Text.Trim(), // Username associated with ticket
DateTime.Now, // Date/time issued
DateTime.Now.AddMinutes(30), // Date/time to expire
true, // "true" for a persistent user cookie
"HR", // User-data, in this case the roles
FormsAuthentication.FormsCookiePath);// Path cookie valid for
// Encrypt the cookie using the machine key for secure transport
string hash = FormsAuthentication.Encrypt(ticket);
HttpCookie cookie = new HttpCookie(
FormsAuthentication.FormsCookieName, // Name of auth cookie
hash); // Hashed ticket
// Set the cookie's expiration time to the tickets expiration time
if (ticket.IsPersistent) cookie.Expires = ticket.Expiration;
// Add the cookie to the list for outgoing response
Response.Cookies.Add(cookie);
// Redirect to requested URL, or homepage if no previous page
// requested
string returnUrl = Request.QueryString["ReturnUrl"];
if (returnUrl == null) returnUrl = "/";
// Don't call FormsAuthentication.RedirectFromLoginPage since it
// could
// replace the authentication ticket (cookie) we just added
Response.Redirect(returnUrl);
}
else
{
// Never tell the user if just the username is password is incorrect.
// That just gives them a place to start, once they've found one or
// the other is correct!
Response.Write( "Username / password incorrect. Please try again.");
}
}
}
}
When I go to a page under the HRPages folder it presents me with the login screen and on successful login it creates a ticket and redirects me back to the page but then reverts back to the login screen again. What am I doing wrong please as it seems as if the role isn't being passed through?
I am using linq to entity connection. I want to keep user logged in once he entered into his account, This is my code. It's not working. Help, please
if (this.ChkRememberme != null && this.ChkRememberme.Checked == true)
{
HttpCookie cookie = new HttpCookie(TxtUserName.Text, TxtPassword.Text);
cookie.Expires.AddYears(1);
Response.Cookies.Add(cookie);
}
if (this.ChkRememberme != null && this.ChkRememberme.Checked == true)
{
int timeout = rememberMe ? 525600 : 30; // Timeout in minutes, 525600 = 365 days.
var ticket = new FormsAuthenticationTicket(TxtUserName.Text, TxtPassword.Text);
string encrypted = FormsAuthentication.Encrypt(ticket);
var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encrypted);
cookie.Expires = System.DateTime.Now.AddMinutes(timeout);// Not my line
cookie.HttpOnly = true; // cookie not available in javascript.
Response.Cookies.Add(cookie);
}
Go to your web.config and find the authentication element. You can set the cookie expiration time (in minutes) there, like such:
<system.web>
<authentication mode="Forms">
<forms loginUrl="~/Account/Login"
name="myCookie" <!-- optional, if you want to rename it -->
timeout="2880" /> <!-- expires in 48 hours -->
</authentication>
</system.web>
Source: how to apply "Remember Me" in c#
Hope this helps
Happy Coding..!!
I recommend to use MembershipReboot for authentication purposes in your app (samples are included).