Set Path dynamically in Forms Authentication - asp.net

Here's the problem we facing.
In a hosted environment setup, we're hosting the same project multiple times. We currently manually specify a Path in the forms config section of our web.config. However, to smooth out our deployment process, we'd like to set the Path depending on the Virtual Directory name.
Is there a way for us to dynamically set the Path in the web.config?

There's an overload of FormsAuthentication.SetAuthCookie that takes the cookie path as a parameter, so if you're handling the login process yourself then you can just pass the path of your choice.
The problem is that the standard System.Web.UI.WebControls.Login will only use the default path value. You could, however, handle the LoggedIn event to fix the path...
void FixCookie( object sender, EventArgs args )
{
Response.Cookies[FormsAuthentication.FormsCookieName].Path = "/my-custom-path";
}

Related

Cookieless ASP.Net sessions for static pages

I'm using ASP.Net cookieless sessions so that the session ID for the application is tracked by placing it in the URL via a 302 redirect, for example if the user were to access the below URL
http://yourserver/folder/default.aspx
They would then be redirect to a URL similar to the following which would then proceed to serve up the actual page content
http://yourserver/folder/(S(849799d1-7ec0-41dc-962d-a77e1b958b99))/default.aspx
The problem I have is that the entry point for the application is actually a static page (e.g. one with a .html extension), and ASP.Net is not issuing a session ID & redirecting the user for this page. This therefore means that links to ASP.Net hosted content (e.g. links, iframes etc...) each result in a new session ID being created for each of these links. I cannot easily change the pages extension for compatability reasons (although this does fix the problem).
How can I prompt ASP.Net to create a session for my page? I've tried adding an explicit handler mapping to ensure that the page is being handled by the ASP.Net modules, however this has no impact - while debugging I can see that a SessionIDManager instance is being created for this page (implying that the ASP.Net is already handling this page via the integrated pipeline regardless of my handler mapping), however ASP.Net is still not creating a session for this page.
I am using IIS 7, however this also needs to work on IIS 6 (with expicit handler mappings) and IIS 8.
I have discovered from experimentation and decompiling the ASP.Net source that the SessionStateModule decides whether or not to create a session for a request based on whether or not the IHttpHandler for the request implements IRequiresSessionState or IReadOnlySessionState. If neither of those are true then its all down to whether or not someone has used SetSessionStateBehavior to set the session state behaviour to either Required or ReadOnly.
By default the static file handler does not do any of these and so the session is not created for static files - to ensure that the session state behaviour is set for the static files that require session state I simply set the session state behaviour during the BeginRequest event for the application
// In Global.asax.cs
void Application_BeginRequest(object sender, EventArgs e)
{
HttpContext.Current.SetSessionStateBehavior(SessionStateBehavior.Default);
}
I'm sure there are better ways, but this worked for me.

Check for a static file during Application_BeginRequest?

I have a Global.asx file that needs to do custom authentication, auditing and profiling stuff. This is needed because it supports a SAML based SSO system and needs to override the normal .Net authentication (which doesn't support either SAML or mixed authentication)
I don't want to fire it for static files, such as .js, .css, .png, etc
In Cassini/WebDev and IIS7 it does.
What I want to have is some simple check, like a this.Request.IsStaticFile (which doesn't exist, unfortunately) to identify the static files.
I realise that this would be fairly simple to write, but it feels like something that must already exist - IIS has already applied caching policy stuff for the static files and so on.
I need a code solution, rather than an IIS config change one.
Update
This is my current workaround:
/// <summary>Hold all the extensions we treat as static</summary>
static HashSet<string> allowedExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
".js", ".css", ".png", ...
};
/// <summary>Is this a request for a static file?</summary>
/// <param name="request">The HTTP request instance to extend.</param>
/// <returns>True if the request is for a static file on disk, false otherwise.</returns>
public static bool IsStaticFile(this HttpRequest request)
{
string fileOnDisk = request.PhysicalPath;
if (string.IsNullOrEmpty(fileOnDisk))
{
return false;
}
string extension = Path.GetExtension(fileOnDisk);
return allowedExtensions.Contains(extension);
}
This works and is quick enough, but feels horribly clunky. In particular relying on extensions is going to be error prone if we add new static files not thought of.
Is there a better way without changing the IIS config?
You might be able to check which handler is dealing with the request.
In IIS6 only .net files, eg aspx are mapped to a handler that does stuff.
In IIS7 with the integrated pipeline, everything routes through .net, which is normally a good thing. Different handlers still deal with different file types though. In particular I believe the staticfilehandler is the one you need to check for. The httpcontext.handler property should allow you to figure it out.
You could create an extension method to add that IsStatic method...
Simon
There are a few options:
Adding authorization element and deny none for those paths that you do not need any authentication and contains your static files
You are using integrated pipeline. Turn it off on your IIS 7.
There is no doubt that you need to create a custom extension method because ASP.NET routing engine uses this code to decide whether a file exist,
if (!this.RouteExistingFiles)
{
string appRelativeCurrentExecutionFilePath = httpContext.Request.AppRelativeCurrentExecutionFilePath;
if (((appRelativeCurrentExecutionFilePath != "~/") && (this._vpp != null)) && (this._vpp.FileExists(appRelativeCurrentExecutionFilePath) || this._vpp.DirectoryExists(appRelativeCurrentExecutionFilePath)))
{
return null;
}
}
You will not able to decide whether the request is static in Application_BeginRequest using context.handler because Routing Module may change the handler and this module always execute after Application_BeginRequest. My suggestion is to use the similar code which ASP.NEt routing engine uses.

ASP.NET Routing - GetRouteData does not work if path exists

I have a HttpModule which intercepts all requests and loads data from the database based on routing rules. However, I run into one problem all the time; GetRouteData only works if the path does not exist:
var routeData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(HttpContext.Current));
Assuming a request comes in for the url http://localhost/contact, I get the correct routing data relating to that url if that path does not exist in the file system. The problem appears when I want to customize the page at that url which I do by creating an aspx page in the path ~/contact/default.aspx. Once I do that, GetRouteData return null.
I have even tried creating a new HttpContext object, but I still can not retrieve route data if the page exists.
Has anyone ever run into this problem? Is there a solution/workaround?
All help will be greatly appreciated.
Set RouteCollection.RouteExistingFiles to true.
public static void RegisterRoutes(RouteCollection routes)
{
// Cause paths to be routed even if they exists physically
routes.RouteExistingFiles = true;
// Map routes
routes.MapPageRoute("...", "...", "...");
}
Beware though. IIS7 behaves a little differently than the server used when debugging within Visual Studio. I got bit by this when I deployed my application to the web. Check out this feedback I submitted to Microsoft Connection.

Cookies. Case Sensitive Paths. How to rewrite URLs

We have a sizable collection of applications (>50) all running under a single domain but with different virtual directories. Pretty standard stuff. We store cookies using paths to segregate cookies by application. Paths are set the the Application Path.
This seems to work fine as long as the casing of the URL is the same as the application path. If it is different, the browser fails to retrieve the collection of cookies.
Is there any very basic way (ISAPI? Global ASAX?) to rewrite all URLs so that they match the Application Path? Ideally this is something that can be configured at the application level.
Currently stuck on IIS6.
thanks
Wondering if this is a possible (even a good) solution:
In Global.asax:
void Application_BeginRequest(object sender, EventArgs e)
{
string url = HttpContext.Current.Request.Url.PathAndQuery;
string application = HttpContext.Current.Request.ApplicationPath;
if (!url.StartsWith(application))
{
HttpContext.Current.Response.Redirect(application + url.Substring(application.Length));
}
}
Use relative URLs in conjunction with a BASE tag might work?

How to change FormsCookieName at runtime in ASP.NET

We would like to have the FormsCookieName of FormsCookiePath change per instance of our application. We have an application which has multiple instances on 1 server/domainname. Because of this we can only work in 1 application at the same time, since the cookies will overwrite eachother. Same for the Sessions btw.
Is there a way to dynamicly, for example in the Global.asax Application_Start, change this name? This would be usefull as we keep a license name in each application which could be used as the basis for the CookieName.
We already work with Web.config and extra files to overwrite Web.config values in external files using: <appSettings file="Web.AppSettings.Config">
But this requires manual actions which can be forgotten and are redundant since the settings can be retrieved from the database.
Thanks.
I had similar situation, I did the following. In the Application_Start, I checked to see if my cookie name needed change. This would occur after a new deployment for all applications where I have the same web.config for all.
protected void Application_Start(object sender, EventArgs e)
{
// determine unique cookie name per application
string cookieName = ...
// Get the web.config forms settings
Configuration c = WebConfigurationManager.OpenWebConfiguration("~");
AuthenticationSection auth = c.GetSection("system.web/authentication")
as AuthenticationSection;
// See if we have mismatch in web.config or in Forms cookiename
if (auth != null && auth.Forms != null &&
(auth.Forms.Name != cookieName
|| FormsAuthentication.FormsCookieName != cookieName
)
)
{
// Assign value in web.config for future restarts
auth.Forms.Name = cookieName;
// would be nice if this restarted the app, but it doesn't appear to
c.Save();
// This seems to restart the app
System.Web.HttpRuntime.UnloadAppDomain();
}
...
}
The web.config is modified on the application start and then the web app is restarted. Next time the web app comes up, cookie names are in sync and the reset code is skipped.
I have been struggling with Cookies with quite a few days. It has been an awesome learning experience.
So wanted to share the possible ways I found & discovered: There are several HACKs to modify Forms Authentication Cookie name:
You can automate the modification of cookie name under Authenticaiton secion of Web.Config file in Application_Start event in Global.asax. Thanks to Ron for sharing this. But I could not guarantee that the user whose identity would be used to run application domain have enough privileges to modify the file on disk or not. Hence I needed an improvised solution, so I devised following.
Thanks to ILSpy for letting me see inside the FormsAuthentication class, and many thanks to Reflection to let me modify the private field of a class. I used following code to modify the cookie name on run-time with following small piece of code and this worked like a charm !!!
protected void Application_Start(Object sender, EventArgs e)
{
// This will enforce that FormsAuthentication class is loaded from configuration settings for the application.
FormsAuthentication.Initialize();
// The new cookie name whatever you need can go here, I needed some value from my application setting to be prefixed so I used it.
string newCookieName = string.Format("{0}.ASPXAUTH", ConfigurationManager.AppSettings["SomeSettingThatIsUniquetoSite"]);
// Modifying underlying baking field that points to FormsAuthentication.FormsCookieName
Type type = typeof(FormsAuthentication);
System.Reflection.FieldInfo field = type.GetField("_FormsName", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
field.SetValue(null, newCookieName);
}
Suggestions, loopholes are requested as this is my first answer on this forum.
According to MSDN, the FormsAuthentication.FormsCookieName property that stores the cookie name is a read-only property. This property must be read from the web.config.
Each instance will need a separate name in the web.config. I suggest including the name of the authentication cookie in your existing change management system.

Resources