How to override authentication for certain web pages....? - c#

Well I am having following code written in master page: -
<authentication mode="Forms">
<forms loginUrl="Loginpage.aspx" />
</authentication>
Now it will redirect to "Loginpage.aspx" if authentication fails.
Now what If I would like to override this authentication for few pages. Also note that the number of pages and page names are not available at design time, so cannot include the aspx page names in configuration file.
Is there anyway to override authentication for few aspx pages?
-Anil

Henrik's answer is a good one and should work if properly implemented. However, this is another option which tackles the problem more from a configuration standpoint. I know that you mentioned that you won't know the page names ahead of time so you can't include an entry in web.config for each page BUT web.config allows you to secure folders too. You could have all pages that require authentication placed in a folder called "AuthRequired" and all pages that don't require authentication placed in a folder called "Anonymous", for example. Then in your web config you could have the following entries:
<location path="AuthRequired">
<system.web>
<authorization>
<deny users="?" />
<allow users="*" />
</authorization>
</system.web>
</location>
<location path="Anonymous">
<system.web>
<authorization>
<allow users="*" />
</authorization>
</system.web>
</location>

You can listen to the AuthorizeRequest event and act accordingly. Create your own Http Module to do this.
Three options:
use the configuration settings above together with generating folders with web.config entries. This is a pretty shoddy way of doing it.
listen to the event AuthenticateRequest, the code looks something like this:
public class UserAuthenticationModule : IHttpModule
{
private HttpApplication _Context;
private RoleManagerModule _RoleManager;
public void Init(HttpApplication context)
{
_Context = context;
context.AuthenticateRequest += AuthenticateUser;
_RoleManager = (RoleManagerModule)context.Modules["RoleManager"];
_RoleManager.GetRoles += roleManager_GetRoles;
}
// http://stackoverflow.com/questions/1727960/how-to-keep-roleprovider-from-overriding-custom-roles
private void roleManager_GetRoles(object sender, RoleManagerEventArgs e)
{
if (_Context.User is UserPrincipal)
e.RolesPopulated = true; // allows roles set in AuthenticateUser to stick.
}
private static void AuthenticateUser(object sender, EventArgs e)
{
var app = (HttpApplication) sender;
if (app.Context == null) return;
var user = app.Context.User;
// not signed in, forms authentication module takes care of redirecting etc.
if (user == null) return;
// we're done then.
if (user is IUser) return;
var userEntity = IoC.Resolve<IUserRepository>().FindByUserName(user.Identity.Name);
// we can't find the user in the database.
if (userEntity == null)
throw new ApplicationException(string.Format("User \"{0}\" deleted from, or renamed in, database while logged into application.",
user.Identity.Name));
// signed in, assigning user, which should assign Thread.CurrentPrincipal as well (it wouldn't do this on PostAuthenticateRequest).
app.Context.User = new UserPrincipal(userEntity);
userEntity.SetAuthenticated();
}
//Implement IDisposable.
public void Dispose()
{
}
}
If your UserPrincipal implements IPrincipal, then IsInRole is used to give role-based access to your pages.
The service-oriented way; set up a small transparent proxy server. List your endpoints/uri-s in a dynamic store, like what you are describing. Set up an authorization service such as Rhino Security; expose its service interfaces as a REST-API or a request/reply interface or something. Assume from the web app's perspective that every request is allowed and take care of where you redirect. In the proxy server, e.g. nginx which is a very nice asynchronous C-based proxy server on Linux, call your authorization service from a filter/module. 'Security in depth' and you can share configuration for security in the Authorization Service.
The principle that you follow is that if something is not allowed in a web application you do throw new HttpException(405, "The current operation you are trying to perform is now allowed for your role or user or chosen path in life") in the AuthorizeRequest event. Note that there's a AuthenticateRequest and another AuthorizeRequest event

You should usually have one point where users can be authenticated - get confirmed that they are who they claim they are. Next, you are probably talking about authorisation, which is a matter of allowing/denying performing certain operation to the user, like sending a GET request. Authorisation rules in a simple scenarios can be configured in the web.config through location element, as presented by Tom.

Related

Dynamic use of cookies in Forms-Authentication

Currently I have 2 ways:
when i set my web.config cookieless="UseCookies" my url looks that:
http://example.com/Stuff
<sessionState timeout="60" cookieless="UseCookies"/>
when i set cookieless="true" i have such urls
http://example.com/%28S%28uanyuxwgaviyonky0lxwq3vq%29%29/Stuff
<sessionState timeout="60" cookieless="true"/>
Is i possible to set cookieless property dynamic? Something like
if(/*condition*/)
{
sessionState .cookieless = "true";
}
else
{
sessionState .cookieless = "UseCookies";
}
This must be somewhere in SessionStart of Global.asax or something
The basic Idea here is you want to modify the WebConfig file at runtime. i didn't tried my-self , but as curiosity i searched over internet , and found This link
and according to this your code in Global.asax will be like
Note : this actually writes the new value the web.config
void Application_Start(object sender, EventArgs e)
{
// Code that runs on application startup
Configuration config;
config = WebConfigurationManager.OpenWebConfiguration("~");
SessionStateSection SessionState = config.GetSection("system.web/sessionState") as SessionStateSection;
if (SessionState != null)
{
SessionState.Mode = System.Web.SessionState.SessionStateMode.InProc;// changes
if (true/*condition*/)
{
SessionState.Cookieless = System.Web.HttpCookieMode.UseCookies;
}
else
{
SessionState.Cookieless = System.Web.HttpCookieMode.UseUri; // not sure about this one
}
config.Save();
}
}
This question is discussed regularly since 2003 (see for example Switch dynamically to cookieless session state asp.net?.
You cannot change cookieless false/true from the application because decision is made by IIS before begin_request fired.
Some available options are:
A.
<system.web>
<sessionState cookieless="AutoDetect"></sessionState>
</system.web>
B.
Set some folder inside your webroot as web application and set cookieless="true"
IIS manage it before application request start so you can not change it on application level.

How does ASP.NET know to create the ASP.NET_SessionId cookie if Session_Start is defined?

I've done quite a bit of testing on this, and I'm thoroughly confused. It seems ASP.NET will generate an ASP.NET_SessionId cookie if the Session_Start method in the MvcApplication class is defined, even if I'm not using the Session variable anywhere. That seems odd, considering there doesn't have to be anything in the method's body.
Example (Global.asax.cs):
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
namespace MyApplication
{
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
private void Session_Start() { } // this is all it takes to generate a SessionId (?)
}
}
Now, I'm confused for multiple reasons:
How is the mere presence of the Session_Start function enough to generate a SessionId? I'm not utilizing the Session variable anywhere in the application, and the method is empty.
The Session_Start method is private, and I'm obviously not calling it anywhere inside the class, so how does ASP.NET know when a session starts?
How does anything outside of this class even know the Session_Start method exists, and to check for a SessionId cookie? It isn't a partial class, and it's explicitly marked private.
I know these reasons sort of blend into one another, but I'm really at a loss as to how this works.
Session_Start is like an event handler for the application. When a new session is created, this method is called by the application. It does not create Session IDs, it is meant as a way for the developer to know when a user visits the site for the first time (that session). This can be used to run some initialization routines or tracking purposes (e.g., we had x number of unique sessions today).
What triggers the creation of the Session and SessionID is a user visiting a page that has session enabled-- ASP.NET will create the Session behind the scenes. The answer to this question has two ways of enabling session state for pages: Session state can only be used when enableSessionState is set to true either in a configuration
In summary:
in web.config, for all pages:
<system.web>
<pages enableSessionState="true" />
</system.web>
in your page.aspx, on a per-page basis (set it to false to turn it off on a per-page basis):
<%#Page enableSessionState="true">
Your web.config should also configure the SessionState mode. This is an example of using the server's memory to store session state:
<sessionState cookieless="false" mode="InProc" timeout="20" />

ClaimsAuthenticationManager in parallel with RoleManagerModule

I'm mixing classic Membership/RoleManager security setup with new WIF 4.5 API for testing purposes. I have implemented two classes that I have breakpoints set on:
public class CustomAuthenticationManager : ClaimsAuthenticationManager
{
public override ClaimsPrincipal Authenticate(string resourceName, ClaimsPrincipal incomingPrincipal)
{
// Breakpoint here is hit 1st
if (!incomingPrincipal.Identity.IsAuthenticated)
{
return base.Authenticate(resourceName, incomingPrincipal);
}
return TransformPrincipal(incomingPrincipal);
}
private ClaimsPrincipal TransformPrincipal(ClaimsPrincipal incomingPrincipal)
{
// this breakpoint is hit last
ClaimsIdentity newIdentity = new ClaimsIdentity("Custom");
newIdentity.AddClaims(incomingPrincipal.Claims);
// I add some additional claims
ClaimsPrincipal newPrincipal = new ClaimsPrincipal(newIdentity);
return newPrincipal;
}
}
public class CustomRoleProvider : RoleProvider
{
public override string[] GetRolesForUser(string username)
{
// breakpoint here is hit 2nd
if(username == "me") return new string [] { "Lead", "Developer" };
return new string[] {};
}
#region Not implemented
// bunch of not implemented methods
#endregion
}
Now result is quite fine, I get mixed ClaimsPrincipal that has both Name claim, Role claims and claims that I've added in TransformPrincipal method.
However, debugging breakpoints are hit in completely weird order:
1) Breakpoint at the beginning of Authenticate method is hit first
2) Breakpoint at the beginning of GetRolesForUser is hit second
3) Breakpoing at the beginning of TransformPrincipal is hit last
Is this just Visual Studio issue or there is an atomic chance that Authenticate might complete before GetRolesForUser is called?
How are RoleManagerModule and ClaimsAuthenticationManager working in the pipeline? In parallel or there is sequential order? Can mixing the two be an issue?
EDIT:
void Application_PostAuthenticateRequest(object sender, EventArgs e)
{
ClaimsPrincipal transformedPrincipal = FederatedAuthentication.FederationConfiguration.IdentityConfiguration.ClaimsAuthenticationManager.Authenticate(null, ClaimsPrincipal.Current);
Thread.CurrentPrincipal = transformedPrincipal;
HttpContext.Current.User = transformedPrincipal;
}
EDIT:
<membership defaultProvider="CustomMembershipProvider">
<providers>
<add name="CustomMembershipProvider" type="Tests.CustomMembershipProvider" />
</providers>
</membership>
<roleManager enabled="true" defaultProvider="CustomRoleProvider">
<providers>
<add name="CustomRoleProvider" type="Tests.CustomRoleProvider" />
</providers>
</roleManager>
<authentication mode="Forms">
<forms loginUrl="~/Account/Login" timeout="2880" />
</authentication>
<system.identityModel>
<identityConfiguration>
<claimsAuthenticationManager type="Tests.CustomAuthenticationManager, Tests"/>
</identityConfiguration>
</system.identityModel>
These two are rather not meant to be used together. SAM role management suppresses forms authentcation role management and as I suspect, your role provider is just enabled in web.config.
I haven't tested this under 4.5 but I don't believe it changed. In 4.0 the authentication manager fires only if a user is not yet authenticated or current user is established with another authentication module. The latter could happen unexpectedly when you issue both cookies, the forms cookie and the sam cookie. Take a look at my blog post on that
http://www.wiktorzychla.com/2012/09/sessionauthenticationmodule-and-dynamic.html
Although I don't know why the breakpoint at the authenticate gets hit first, SAM would notice that your user is RolePrincipal with FormsIdentity and would try to make a ClaimsPrincipal out of it and then call the authentication manager.
Edit: after all you provided, I still have a slight problem regarding the order. Although Dominick suspects roles are lazy loaded, I can't confirm this by recompiling the BCL. Rather, the RolePrincipal seems to eagerly add role claims in the constructor (the internal AttachRoleClaims method in the RolePrincipal class).
No matter how I look into it, I believe the GetRolesForUser should be called first, as this is ClaimsPrincipal.Current passed to the Authenticate.
If your RoleProvider creates the RolePrincipal in advance, the AttachRoleClaims would be called and roles would be enumerated with the RoleProvider.GetRolesForUser.
If your RoleProvider doesn't create the RolePrincipal in advance, the ClaimsPrincipal.Current would and this leads to the same execution path.
Edit2: I've found the possible culprit, it is the RoleClaimProvider::Claims method that is internally used by the AttachRoleClaims. It is implemented lazily with yield which means that until someone actually enumerates role claims, they are not created. This "someone" is your code from the TransformPrincipal:
ClaimsIdentity newIdentity = new ClaimsIdentity("Custom");
/* this forces the enumeration */
newIdentity.AddClaims(incomingPrincipal.Claims);
However, this would also mean that the GetRolesForUser would be called last, from within the TransformPrincipal.

How to redirect user while an error happens during OnApplicationStarted?

I am doing a system version comparison during OnApplicationStarted method in Global.asax which does not allow the system boot up if the database version and system version are not matching.
So it looks like this:
protected override void OnApplicationStarted()
{
try{
if(systemVersion != dbVersion)
throw new Exception("They are not same!");
}
catch{
//do some other things
//Response.Redirect("~/VersionError.html");
}
}
but it says "Response is not available in this context." I tried to catch the error in Application_Error but I got the same error.
My question is that how I can redirect users to an error page from within these methods?
Edit:
I know there is no Response at this time but I was asking how to get around this problem. And also one of the reasons why I can hit methods after OnApplicationStarted is that we don't want to load many things if this exception occurs.
Since you don't have access to the Response when the application starts but want all incoming requests to go to an error page if a condition is met, you need to set up something farther up the pipeline. Think IIS modules. Install a module via the following:
<system.webServer>
<modules>
<add name="VersionsNotSameHandler"
type="Company.Exceptions.VersionsNotSameHandler, Company.Exceptions"
preCondition="managedHandler" />
</modules>
</system.webServer>
Now for the module code:
namespace Company.Exceptions
{
public class VersionsNotSameHandler: IHttpModule
{
public void Dispose() { }
public void Init(HttpApplication application)
{
context.PreRequestHandlerExecute += newEventHandler(OnPreRequestHandlerExecute)
}
public void OnPreRequestHandlerExecute(Object source, EventArgs e){
HttpApplication app = (HttpApplication)source;
HttpRequest request = app.Context.Request;
// Check for invalid versioning
app.Response.Redirect('.html page displaying error', true);
}
}
}
The OnPreRequest occurs before every request, making sure that the versions are always checked...you may want to cache if you notice things slowing down.
There is no response at this point you are in application start! This is when the application is starting not when a response is being made.
You could set something in here to direct every url to an error page if this condition is met by setting up a default route which will catch all requests. This would kind of give you the functionality you want as future requests would then get an error page.
Can you just throw an HTTP error:
throw new HttpException(403, "Forbidden");
And then in your web.config:
<customErrors mode="On">
<error statusCode="403" redirect="~/VersionError.html"/>
</customErrors>

System.Web.Http.AuthorizeAttribute not recognizing custom role provider

In my MVC 4 Web API project, I have a custom role provider that works as designed via System.Web.Mvc.Authorize attribute on my Home System.Web.Mvc.Controller.
On any System.Web.Http.ApiController with System.Web.Http.Authorize the custom role provider never gets called, always returning false. Is there a way to specify that the Web API AuthorizeAttribute pick up my custom role provider like the MVC AuthorizeAttribute?
Role Provider:
public class CustomRoleProvider : RoleProvider
{
//Overriden methods
public override string[] GetRolesForUser(string username)
{
//Always return "Master" for testing purposes
return new string[] { "Master" };
}
public override bool IsUserInRole(string username, string roleName)
{
//Always return true for testing purposes
return true;
}
//Other overridden method stubs...
}
Web.config:
<roleManager defaultProvider="CustomRoleProvider" enabled="true" cacheRolesInCookie="false" >
<providers>
<clear />
<add name="CustomRoleProvider" type="MyApp.SecurityExtensions.CustomRoleProvider, MyApp" />
</providers>
</roleManager>
This is not really an answer, but this might help:
Both attributes work by querying the current pricipal. The MVC attribute uses HTTPContent.User, while the System.Web.http version uses Thread.CurrentPrincipal, but that difference is minor.
I'm not really familar with Web API, but I suspect that the RoleManagerModule is not running by the time the attribute fires, or you have not yet reached the PostAuthenticateRequest event, because in that event the Module replaces the Pricipal.
Are you sure you have some form of ASP authentication required for your WebAPI usage? If you don't have your WebAPI project configured to require some form of authentication, then obviously you will never reach the PostAuthenticateRequest event, and thus the RoleManagerModule will never kick-in.
The last possibility that comes to mind is that someting else is replacing the Principal after the RoleManagerModule does so. If possible, temporarally remove the System.Web.Http.AuthorizeAttribute, set a breakpoint in the controller, and detemine what class Thread.CurrentPrincipal has. That might give you a hint as to where it went wrong.
You would need to use System.Web.Http.AuthorizeAttribute for Web API's controllers. Sample: http://www.piotrwalat.net/basic-http-authentication-in-asp-net-web-api-using-membership-provider/

Categories