I need an app pool recycle to be completely transparent to the users of my web app.
Currently, upon an IIS 7 App Pool recycle all users logged into my web app are kicked out and are required to log back in (Context.User.Identity.IsAuthenticated is set to false). I employ SQL State Server, I use forms authentication and both are configured to use cookies. I was under the impression that .NET and/or IIS handles authentication of cookies.
However, every time the app pool is recycled Context.User.Identity.IsAuthenticated is set to false (and I've no idea where this occurs) my users are kicked out and are required to log back in. I can see that the session id remains the same throughout logins, I can also view this session information in the database/state server.
I can't tell if this is a session or a cookie problem.
Please Help!
Logon method:
public ActionResult LogOn(string userName, string password, bool rememberMe, string returnUrl)
{
if (!ValidateLogOn(userName, password))
{
return View();
}
FormsAuth.SignIn(userName, true); // uses FormsAuthentication.SetAuthCookie(username, true);
Session["userName"] = userName;
if (!String.IsNullOrEmpty(returnUrl))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
Custom Controller Attribute:
public class CookieAuthorizeAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
HttpContext lvContext = HttpContext.Current;
if (!lvContext.User.Identity.IsAuthenticated)
{
lvContext.Response.Redirect("~/Account/Logon");
}
else
{
FormsIdentity identity = (FormsIdentity)HttpContext.Current.User.Identity;
FormsAuthentication.RenewTicketIfOld(identity.Ticket);
}
base.OnActionExecuting(filterContext);
}
}
WebConfig:
<authentication mode="Forms">
<forms cookieless="UseCookies" loginUrl="~/Account/LogOn" slidingExpiration="true" name=".ASPXAUTH" requireSSL="false" timeout="2880" />
</authentication>
<modules runAllManagedModulesForAllRequests="true">
<remove name="ScriptModule" />
<remove name="UrlRoutingModule" />
<remove name="Session" />
<remove name="FormsAuthentication" />
<add name="ScriptModule" preCondition="managedHandler" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<add name="NHibernateMvcSessionModule" type="EpnNHibernateBase.NHibernateMvcSessionModule, EpnNHibernateBase" />
<add name="Session" type="System.Web.SessionState.SessionStateModule" />
<add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule" />
</modules>
I was able to find a solution on my own. The problem was not with how authentication was handled programmatically or how I was authenticating users. The problem was with how I had configured the authentication in IIS/web.config.
I carefully followed the steps in the links listed below:
Configuring Forms Authentication (IIS 7) (Branching out on every related section)
Configuring Machine Keys in IIS 7 <-- This one in particular
After having followed those steps closely I was able to correctly generate a machine key.
This machine key is as follows (with a fabricated key):
<machineKey decryptionKey="ASDF3WS545AS5D4F8254A12DAFA5SDF7,IsolateApps" validation="3DES" validationKey="A65A6S5DF46ASD4F89WEF6SAD2F4A68EF4AW65F4D3A2F4AS6DF89A98D4F6A5SD4F6A5SDF46ASD8F4A6S5DF46AS5D4F6AS5DF49AS8DF46AS5D4F6AS5DF46SAD5F,IsolateApps" />
Additionally, httpModules and system.webServer:modules sections in the web.config required the addition of the following modules:
<remove name="Session" />
<remove name="FormsAuthentication" />
<add name="Session" type="System.Web.SessionState.SessionStateModule" />
<add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule" />
Summary: From what I gather, cookies were being created and encrypted, but because there was no machine key I was unable to unencrypt the cookie, thus causing the need to reauthenticate.
have you tried storing the sessions in a database so they are persistent even if the process is recycled?
see here for howto
Reading your post closely, it looks like the real issue is that users are not logged back in automatically. This is handled by cookies assuming you are using forms authentication.
Once a user is logged in, their session state will be restored if you are using SQL to persist it.
To debug cookies, you can use Fiddler or other cookie sniffers.
Posting your web.config would be helpful as well.
Related
2 for my ASP.NET web pages .NET 4.0 app. I was using Enterpriselibrary to log and send error via email before the app was upgraded to .NET 4.0 but since its upgraded the EnterpriseLibrary logging has stopped logging so I have decided to switch to Elmah.
In part of my web.config file of the app:
<add name="ErrorHandler" type="myWebApp.ErrorHandlingModule, myWebApp" preCondition="managedHandler" />
<add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah" preCondition="managedHandler" />
<add name="ErrorMail" type="Elmah.ErrorMailModule, Elmah" preCondition="managedHandler" />
<add name="ErrorFilter" type="Elmah.ErrorFilterModule, Elmah" preCondition="managedHandler" />
As you can see there is already an Errorhandling module there (ErrorHandlingModule), which creates a custom error message ("ex" object in the following code). Originally in the ErrorHandlingModule:
try
{
ExceptionPolicy.HandleException(ex, "myWebApp Error Handling");
}
catch (Exception exApplicationBlockError)
{
try
{
EventLog log = new EventLog("Application");
log.Source = "Application Error";
log.WriteEntry(exApplicationBlockError.ToString(), EventLogEntryType.Error);
}
catch
{
}
}
Since I switch to Elmah:
try
{
//ExceptionPolicy.HandleException(ex, "myWebApp Error Handling");
Elmah.ErrorSignal.FromCurrentContext().Raise(ex);
}
catch (Exception exApplicationBlockError)
{
try
{
EventLog log = new EventLog("Application");
log.Source = "Application Error";
log.WriteEntry(exApplicationBlockError.ToString(), EventLogEntryType.Error);
}
catch
{
}
}
It works very well but the problem now is if there is any error it raises 2 error emails: one is the custom one, and the other the default Elmah one; and of course I only want the custom one.
I haven't added any extra Elmah ErrorFilter in the program but I do need to have the custom exception in my email.
Should I add a custom Elmah filter to filter out ALL the exceptions so Elmah won't send me the default error email? (Or any other suggestions?)
Also attach the elmah config in web.config file if it is any help:
<elmah>
<security allowRemoteAccess="false" />
<errorMail from="xxxx#myorg.au" to="xxxxx#myorg.au" subject="myWebApp error" async="true" />
</elmah>
Thanks in advance. Happy holiday!
If you don't want Elmah to email, then don't configure it to do so in the first place. Remove the module configuration:
<add name="ErrorMail" type="Elmah.ErrorMailModule, Elmah">
And any other ErrorMail Elmah email related configuration such as
<errorMail from="xxxx#myorg.au" to="xxxxx#myorg.au" subject="myWebApp error" async="true" />
Silly me I should not need to include:
Elmah.ErrorSignal.FromCurrentContext().Raise(ex);
It's why the email was sent twice. But thanks for the help.
I am working on an asp.net mvc web application, and I have added the following provider to my asp.net web.config:
<system.web>
<membership>
<providers>
<add name="TestDomain1ADMembershipProvider" type="System.Web.Security.ActiveDirectoryMembershipProvider,System.Web, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a,Version=4.0.0.0" connectionStringName="TestDomain1ConnectionString" connectionUsername="ad-domainA.intra\it360ad.user" connectionPassword="$$$$$3" />
</providers>
</membership>
&
<add name="TestDomain1ConnectionString"
connectionString="LDAP://10.211.12.30.ad-domainA.intra/CN=Users,DC=ad-domainA,DC=intra" />
but when the users try to access the application and they enter username and password , this will raise the following exception :
Unable to establish secure connection with the server
So what might be the problem? and also is it right to include my server IP address inside the connection string as I am doing ?
EDIT
I changed my setting to be:
<system.web>
<trust level="Full" originUrl="" />
<membership>
<providers>
<add name="TestDomain1ADMembershipProvider" type="System.Web.Security.ActiveDirectoryMembershipProvider,System.Web, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a,Version=4.0.0.0" connectionStringName="TestDomain1ConnectionString" connectionUsername="it360ad.user" connectionPassword="$$$$$" />
</providers>
</membership>
&
<add name="TestDomain1ConnectionString"
connectionString="LDAP://ad-domainA.intra/OU=TM,DC=ad-doaminA,DC=intra" />
but currently the following check
if(domainProvider.ValidateUser(model.UserName, model.Password)
inside the Account controller action method will return
The user name or password provided is incorrect
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model, string returnUrl)
{
MembershipProvider domainProvider;
domainProvider = Membership.Providers["TestDomain1ADMembershipProvider"];
// Validate the user with the membership system.
if (domainProvider.ValidateUser(model.UserName, model.Password))
{
//code goes here
}
else
{
ModelState.AddModelError("", "The user name or password provided is incorrect.");
return View(model);
}
Can you advice why the validation will always fail ?
Thanks
Try using this connectionString="LDAP://10.211.12.30:389 />. I had the same problem and I found that I had to remove anything else after :389in the connection string.
I dont know why that is but it worked for me..... Hope this helps
I am trying to get an ASP.NET MVC 4 page working on a productive system. On my local system with IIS 7 and Windows 7 Professional everything works fine.
Productive system:
Windows Web Server 2008 R2 64 bit
IIS 7 with .NET 4
For the page I created a new IIS site. For the site i created a new application pool which uses: .NET Framework v4.0.30319 and integrated mode. For the application pool the flag for 32 bit applications is activated, because i need to run my application as 32 bit app.
When I access the page via
https://localhost:12345/PageName
i get the error message:
(I translate it from german:)
Server error in application /PageName
The resource could not be found
Description: HTTP 404. The resource or a dependency could not be found...
Requested URL: /PageName
Version: .NET 4.0.30319; ASP.NET 4.0.30319.1
The routing table:
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute("Default", "{controller}/{action}/{id}", new { controller = "MyController", action = "Index", id = "" });
The Index method of the controller looks like this
[ValidateInput(false)]
[RequireHttps(Order = 1)]
public ActionResult Index(string method, string chapterId, string sessionKey, string cn, string ui, string pw, string ProductNumber, string DocumentData)
{
// something...
}
web.config
...
<system.web>
<compilation debug="true" defaultLanguage="c#" targetFramework="4.0" />
<authentication mode="Forms">
<forms protection="All" loginUrl="~/Account/Login" timeout="1" />
</authentication>
<httpRuntime requestValidationMode="2.0" />
<sessionState mode="Off" />
</system.web>
...
<system.webServer>
<validation validateIntegratedModeConfiguration="false" />
<modules runAllManagedModulesForAllRequests="true" />
<handlers>
<remove name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" />
<remove name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" />
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<add name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" />
<add name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" />
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
</system.webServer>
For debugging purpose I added the http://haacked.com/archive/2008/03/13/url-routing-debugger.aspx to the Application_Start function of the global.asax file.
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
// added call here
}
The route is solved correct. --> Controller = "MyController", Action = "Index", Id = ""
IIS log
2012-12-12 15:21:04 ::1 GET /PageName - 49266 - ::1 Mozilla/5.0+(compatible;+MSIE+9.0;+Windows+NT+6.1;+WOW64;+Trident/5.0) 404 0 0 656
2012-12-12 15:32:18 ::1 GET /PageName - 49266 - ::1 Mozilla/5.0+(compatible;+MSIE+9.0;+Windows+NT+6.1;+WOW64;+Trident/5.0) 404 0 0 0
2012-12-12 15:37:48 ::1 GET /PageName - 49266 - ::1 Mozilla/5.0+(compatible;+MSIE+9.0;+Windows+NT+6.1;+WOW64;+Trident/5.0) 404 0 0 5687
Windows EventLog doesnt show anything more.
I'm trying to get custom HttpHandler working in my sample web application. I've been experiencing a lot of issues, but finally got stuck with error 500. Application pool is being run in Classic ASP.NET 2.0 mode. Server is IIS 7.5, OS is Win 7 Pro.
Here's a code of my handler:
public class SampleHandler : IHttpHandler
{
public SampleHandler()
{
}
public bool IsReusable
{
get
{
return true;
}
}
public void ProcessRequest(HttpContext context)
{
context.Response.Clear();
context.Response.ContentType = "text/html";
context.Response.Write("This is a sample content.");
context.Response.Expires = 0;
context.Response.End();
}
}
Here is a web.config file content:
<?xml version="1.0"?>
<configuration>
<system.web>
<httpHandlers>
<add verb="*" path="*.shc" type="SampleHandler"/>
</httpHandlers>
</system.web>
<system.webServer>
<handlers>
<add resourceType="Unspecified" verb="*" path="*.shc" name="SampleHandler" type="SampleHandler" modules="IsapiModule" scriptProcessor="c:\Windows\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll\aspnet_isapi.dll"/>
</handlers>
</system.webServer>
</configuration>
Here is a link to the screenshot of an error : http://bit.ly/cmPk4i
Could anybody please tell me what I did wrong? Thanks in advance!
Try setting
<validation validateIntegratedModeConfiguration="false" />
in
<system.webServer>
I had 500 error and this fixed it.
<system.webServer>
<validation validateIntegratedModeConfiguration="false" />
<handlers>
<add .... />
</handlers>
</system.webServer>
From the list of "things you can try", did you install the .Net Extensibility Feature?
You can also enable the Failed Requests logging feature on the application, which provides detailed information on request processing.
The good news, at least, is that your registered handler is recognized as the handler to be executed.
I have this problem; I'm developing a site with ASP.Net 2005, the database I use is MySQL and the Web Server is Cassini, also I use Forms Authentication to handle the access to the pages.
I was making tests in all the computers accessing the site, however yesterday when I accessed the site from a PC the login page is presented but when I press the button to authenticate I stay in the same login page.
I don't know what is going because I can access the pages in the server but accessing from any other terminal it keeps me in the login page without accessing to the site (program) itself.
What is wrong here?
This is the code of the login button
qfh.User user = qfh.Global.Login(txtUserName.Text, txtPassword.Text, null, null);
if (user != null)
{
// Initialize FormsAuthentication, for what it's worth
FormsAuthentication.Initialize();
// Create a new ticket used for authentication
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
1, // Ticket version
user.UserName, // Username associated with ticket
DateTime.Now, // Date/time issued
DateTime.Now.AddMinutes(30), // Date/time to expire
true, // "true" for a persistent user cookie
string.Join(",", user.GetRoles()), // User-data, in this case the roles
FormsAuthentication.FormsCookiePath);// Path cookie valid for
//Fill the complementary data
Profile.User = user.UserName;
Profile.Name = user.Name;
//Profile.Enterprise = user.Enterprise.EnterpriseCode; // enterprise.EnterpriseCode;
//Profile.Period = user.Enterprise.GetActivePeriod().PeriodCode; //enterprise.GetActivePeriod().PeriodCode;
Session["Enterprise"] = user.Enterprise.EnterpriseCode;
Session["Period"] = user.Enterprise.GetActivePeriod().PeriodCode;
// 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
{
lblStatusMessage.Text = Utilities.JSAlert("Access denied");
return;
}
This is the web.config
<?xml version="1.0"?>
<!--
Note: As an alternative to hand editing this file you can use the
web admin tool to configure settings for your application. Use
the Website->Asp.Net Configuration option in Visual Studio.
A full list of settings and comments can be found in
machine.config.comments usually located in
\Windows\Microsoft.Net\Framework\v2.x\Config
-->
<configuration>
<configSections>
<section name="activerecord" type="Castle.ActiveRecord.Framework.Config.ActiveRecordSectionHandler, Castle.ActiveRecord"/>
</configSections>
<appSettings>
<add key="QFH" value="QFH2009" />
</appSettings>
<activerecord isWeb="true">
<config>
<add key="hibernate.connection.driver_class" value="NHibernate.Driver.MySqlDataDriver"/>
<add key="hibernate.dialect" value="NHibernate.Dialect.MySQLDialect"/>
<add key="hibernate.connection.provider" value="NHibernate.Connection.DriverConnectionProvider"/>
<add key="hibernate.connection.connection_string" value="Server=localhost;Database=qfh;User ID=root;Password=admin;Pooling=false;Min Pool Size=5;Max Pool Size=100;"/>
</config>
</activerecord>
<connectionStrings>
<!--<add name="QFHConnectionString" connectionString="Dsn=QFH" providerName="System.Data.Odbc"/>-->
<add name="QFHConnectionString" connectionString="Server=localhost;Database=qfh;User ID=root;Password=admin;Pooling=false;Min Pool Size=5;Max Pool Size=100;"/>
</connectionStrings>
<system.web>
<roleManager defaultProvider="MySqlRoleProvider"
enabled="true"
cacheRolesInCookie="true"
cookieName=".ASPROLES"
cookieTimeout="30"
cookiePath="/"
cookieRequireSSL="false"
cookieSlidingExpiration="true"
cookieProtection="All" >
<providers>
<clear />
<add
name="MySqlRoleProvider"
type="Andri.Web.MySqlRoleProvider"
connectionStringName="QFHConnectionString"
applicationName="QFH"
writeExceptionsToEventLog="true"
/>
</providers>
</roleManager>
<membership defaultProvider="MySqlMembershipProvider" userIsOnlineTimeWindow="15">
<providers>
<clear />
<add
name="MySqlMembershipProvider"
type="Andri.Web.MySqlMembershipProvider"
connectionStringName="QFHConnectionString"
applicationName="QFH"
enablePasswordRetrieval="false"
enablePasswordReset="true"
requiresQuestionAndAnswer="true"
requiresUniqueEmail="true"
passwordFormat="Hashed"
writeExceptionsToEventLog="true"
/>
</providers>
</membership>
<!--
Set compilation debug="true" to insert debugging
symbols into the compiled page. Because this
affects performance, set this value to true only
during development.
-->
<httpModules>
<add name="ar.sessionscope" type="Castle.ActiveRecord.Framework.SessionScopeWebModule, Castle.ActiveRecord"/>
</httpModules>
<compilation debug="true">
<assemblies>
<add assembly="MySql.Data, Version=5.1.7.0, Culture=neutral, PublicKeyToken=C5687FC88969C44D"/>
<add assembly="System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
<add assembly="System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>
<add assembly="System.Transactions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
<add assembly="Microsoft.VisualBasic, Version=8.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>
</assemblies>
</compilation>
<!--
The <authentication> section enables configuration
of the security authentication mode used by
ASP.NET to identify an incoming user.
-->
<!--<roleManager enabled="false"/>-->
<authentication mode="Forms">
<forms name="QFHWEBAPP.ASPXAUTH" loginUrl="Login.aspx" defaultUrl="Default.aspx" />
</authentication>
<authorization>
<!-- Do not allow all users come in -->
<deny users="?"/>
</authorization>
<anonymousIdentification enabled="true"/>
<!-- Temporary fields for the session -->
<profile defaultProvider="MySQLProfileProvider">
<providers>
<!--<add name="MySqlProfileProvider"
type="Malachi.MySqlProviders.MySqlProfileProvider"-->
<add name="MySQLProfileProvider"
type="Ezim.MySql.Web.Profile.MySqlProfileProvider"
connectionStringName="QFHConnectionString"
applicationName="QFH"/>
</providers>
<properties>
<add name="User" allowAnonymous="true" type="System.String"/>
<add name="Name" allowAnonymous="true" type="System.String"/>
<add name="Period" allowAnonymous="true" type="System.Int32"/>
<add name="Enterprise" allowAnonymous="true" type="System.Int32"/>
</properties>
</profile>
<!--
The <customErrors> section enables configuration
of what to do if/when an unhandled error occurs
during the execution of a request. Specifically,
it enables developers to configure html error pages
to be displayed in place of a error stack trace.
<customErrors mode="RemoteOnly" defaultRedirect="GenericErrorPage.htm">
<error statusCode="403" redirect="NoAccess.htm" />
<error statusCode="404" redirect="FileNotFound.htm" />
</customErrors>
-->
<customErrors mode="Off" />
</system.web>
<!--This code is used to make available the css-->
<location path="css">
<system.web>
<authorization>
<allow users="*"/>
</authorization>
</system.web>
</location>
</configuration>
First rule out issues with the PC can you run fiddler (google it some MS devs wrote it) on the pc to check that the submit is getting processed by the server. If its not going to the web server then it could be a proxy issue blocking the pc from seeing your site or a javascript permissions issue stopping the button from being submitted.
If it is connecting then i would check the db query is going through (you did change the username and password in the web.config above i hope.) If that is ok; are your page permission settings correct; my sites web.config has a lot more authorisation settings in it.
<location path="css">
<system.web>
<authorization>
<allow users="*"/>
</authorization>
</system.web>
</location>
</configuration>