Custom Authentication Module inside MVC3 web app - asp.net-mvc

Context:
We have an internal Asp.Net web application which is configured to use windows authentication. As part of this authentication aspect, we have an HttpModule that essentially grabs the HttpContext.Current.Identity.Name and returns a UserInfo object which get dropped into the HttpContext.Items collection.
In migrating this over MVC3, I have a base controller and OnActionExecuting, I am unable to see this UserInfo item in the collection at all. Any insight would be great. Here's my setup:
BaseController:
protected override void OnActionExecuting(ActionExecutingContext ctx)
{
if (ctx.HttpContext.Items["UserInfo"] != null)
{
UserInfo currentUser = (UserInfo)ctx.HttpContext.Items["UserInfo"];
dynamic viewBag = ctx.Controller.ViewBag;
viewBag.CurrentUser = currentUser;
}
else
{
// Unauthorized do something
}
base.OnActionExecuting(ctx);
}
web.config:
<system.web>
<httpModules>
<add type="WFS.SIG.Client.Security.Authentication.WindowsAuthentication, WFS.SIG.Client.Security" name="AuthenticationModule"/>
</httpModules>
</system.web>....
<system.webServer>
<validation validateIntegratedModeConfiguration="false"/>
<modules runAllManagedModulesForAllRequests="true">
<add name="AuthenticationModule" type="WFS.SIG.Client.Security.Authentication.WindowsAuthentication, WFS.SIG.Client.Security" />
</modules>
</system.webServer>

I think your code should look like this:
protected override void OnActionExecuting(ActionExecutingContext ctx)
{
if (ctx.HttpContext.Items["UserInfo"] != null)
{
UserInfo currentUser = (UserInfo)ctx.HttpContext.Items["UserInfo"];
ViewBag.CurrentUser = currentUser;
}
else
{
// Unauthorized do something
}
base.OnActionExecuting(ctx);
}
The access to HttpContext should work like this. But you can access the ViewBag directly.
Can you check whether your authenticaton module is really called and does store an object in the HttpContext? Can you set a breakpoint?

Related

Redirecting to Login Page Automatically without doing Action in Controller ASP.NET MVC when session expired

I know it's a common question but I'm really exhausted of searching a complete solution without success. I have done an attribute filter but this works only if the user interact with the application and I don´t want this, I want when session expired, automatically the browser load the login page. Maybe someone could show what code should I use in javasccript or html .... and the configuration that I should use in the web.config if necesary. Thanks in advance.
This is my actual attribute:
[AttributeUsage(AttributeTargets.Method)]
public class SessionExpireCustom : FilterAttribute, IActionFilter
{
public void OnActionExecuting(ActionExecutingContext filterContext)
{
if (HttpContext.Current.Session[HttpContext.Current.Session.SessionID + "_authstatus"] == null)
{
string redirectTo = "~/Account/Login";
if (!string.IsNullOrEmpty(HttpContext.Current.Request.RawUrl))
{
redirectTo = string.Format("~/Account/Login?ReturnUrl={0}", HttpUtility.UrlEncode(HttpContext.Current.Request.RawUrl));
filterContext.Result = new RedirectResult(redirectTo);
return;
}
filterContext.Result = new RedirectResult("~/Account/Login");
return;
}
}
public void OnActionExecuted(ActionExecutedContext filterContext)
{
return;
}
}
And in my web.config:
<system.web>
<sessionState mode="InProc" cookieless="false" timeout="20" />
<compilation debug="true" targetFramework="4.6.1" />
<httpRuntime targetFramework="4.6.1" maxRequestLength="102400"/>
<!--KiloBytes-->
<httpModules>
<add name="ApplicationInsightsWebTracking" type="Microsoft.ApplicationInsights.Web.ApplicationInsightsHttpModule, Microsoft.AI.Web" />
</httpModules>
</system.web>
Here is my solution. You can refer. It makes count time after user last action, not first visit on site.
In _Layout, add this:
<script>
//session end
var sessionTimeoutWarning = #Session.Timeout- 1; //when session end 1 minute before this code makes redirect
var sTimeout = parseInt(sessionTimeoutWarning) * 60 * 1000;
setTimeout('SessionEnd()', sTimeout);
function SessionEnd() {
window.location = "/Account/LogOff"; //Redirect to LogOff, and then redirect to Login
}
</script>
--You need to clear Session in LogOff method.
public ActionResult LogOff()
{
Session.Clear();
Session.Abandon();
FormsAuthentication.SignOut();
return RedirectToAction("Login", "Account");
}
Hope to help, my friend :))

Serving static files in ASP.NET to authorized users only

I am trying to secure a folder under my project that just has some static files, a combination of .htm and .js files. I have tried creating a custom HttpHandler like:
public class StaticFilesHttpHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
if (context.Request.IsAuthenticated)
{
// continue with the request
}
else
{
throw new HttpResponseException(HttpStatusCode.Unauthorized);
}
}
public bool IsReusable => false;
}
Then register it to be used with a route via Route.Config
routes.RouteExistingFiles = true;
routes.Add("helpRoute", new Route("folder/*.htm", new StaticFilesRouteHandler ()));
and a route handler to provide the
public class StaticFilesRouteHandler : IRouteHandler
{
public IHttpHandler GetHttpHandler(RequestContext context)
{
return new StaticFilesHttpHandler ();
}
}
and also via web.config under system.webServer
<handlers>
<add name="StaticFileHandler" verb="GET" path="~/help/default.htm" type="StaticFilesHttpHandler "/>
</handlers>
Files in the folder are provided by a 3rd party. I am to call a function inside a js file in the folder which then redirects the user to a proper .htm file inside it's sub structure. I do not want users to be able to type the url and access any of the files. What am I doing wrong?
can you change the type to TransferRequestHandler and make sure your path is correct.
<handlers>
<add name="StaticFileHandler" verb="GET" path="~/help/default.htm" type="TransferRequestHandler" />
</handlers>
in your global.asax file you can access the request in Application_BeginRequest to verify if the request is authenticated or not.

umbraco MVC custom routes using a dot in url

I have a problem with using a dot in url umbraco MVC custom routes.
/logo/images/image.jpg?width=100 gives following errors:
[NullReferenceException: Object reference not set to an instance of an object.]
Umbraco.Web.Mvc.UmbracoVirtualNodeByIdRouteHandler.FindContent(RequestContext requestContext, UmbracoContext umbracoContext) +18
Umbraco.Web.Mvc.UmbracoVirtualNodeRouteHandler.GetHttpHandler(RequestContext requestContext) +48
System.Web.Routing.UrlRoutingModule.PostResolveRequestCache(HttpContextBase context) +11987058
System.Web.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +141
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +91
/logo/images/image.jpg/?width=100
Works, but this isn’t a good solution for me.
I have tried adding this in webconfig
<location path="logo">
<!-- This only applies it to the relevant path and keeps the protection in place for elsewhere -->
<system.web>
<httpHandlers>
<add path="/images/*" type="System.Web.Handlers.TransferRequestHandler" verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" />
</httpHandlers>
</system.web>
<!-- Required for IIS 7.0+ -->
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" />
<validation validateIntegratedModeConfiguration="false" />
<handlers>
<add name="ApiURIs-ISAPI-Integrated-4.0" path="*" type="System.Web.Handlers.TransferRequestHandler" verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
</system.webServer>
</location>
taken from https://average-joe.info/allow-dots-in-url-iis/
but it won't work:(
My custom route looks like this:
protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
{
//custom route
RouteTable.Routes.MapUmbracoRoute(
"images",
"logo/{action}/{key}",
new
{
controller = "Image",
key = UrlParameter.Optional,
},
new ProductsRouteHandler(4884));
}
}
public class ProductsRouteHandler : UmbracoVirtualNodeByIdRouteHandler
{
public ProductsRouteHandler(int realNodeId) : base(realNodeId)
{
}
protected override IPublishedContent FindContent(RequestContext requestContext, UmbracoContext umbracoContext, IPublishedContent baseContent)
{
return base.FindContent(requestContext, umbracoContext, baseContent);
}
}
I'am using umbraco vs.7.4.3
The UmbracoModule ignores Urls with a file extension, so an UmbracoContext will never get created for a request containing a file extension.
You can create a context using UmbracoContext.EnsureContext, however if you did this in FindContent method of your handler, you'd encounter this exception. This is caused by a stale variable on line 18 of the UmbracoVirtualNodeRouteHandler holding a reference to a null UmbracoContext, and doesn't pick up the freshly created context.
The following is how worked around it so I could call EnsureContext before the VirtualNodeRouteHandler gets called.
var route = routes.MapRoute("RouteName", "some/url/file.ext", new
{
controller = "MyController",
action = "Index"
}
route.RouteHandler = new UrlWithExtensionHandler();
Notice its not the MapUmbracoRoute, but the standard MVC Map Route, and a standard MVC IRouteHandler which calls EnsureContext before returning an instance of a UmbracoVirtualNodeRouteHandler.
public class UrlWithExtensionHandler : IRouteHandler
{
#region Implementation of IRouteHandler
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
// init umbraco context
var httpContext = new HttpContextWrapper(HttpContext.Current);
UmbracoContext.EnsureContext(
httpContext,
ApplicationContext.Current,
new WebSecurity(httpContext, ApplicationContext.Current),
UmbracoConfig.For.UmbracoSettings(),
UrlProviderResolver.Current.Providers,
false);
var handler = new UrlWithExtensionVirtualNodeRouteHandler();
return handler.GetHttpHandler(requestContext);
}
#endregion
}
public class UrlWithExtensionVirtualNodeRouteHandler : UmbracoVirtualNodeRouteHandler
{
protected override IPublishedContent FindContent(RequestContext requestContext,
UmbracoContext umbracoContext)
{
return someIPublishedContent;
}
}
Not an ideal solution, but a valid workaround until the stale variable issue gets merged into core - I've submitted a PR to fix it
A few others have had the same issue too http://issues.umbraco.org/issue/U4-9384

SimpleMembership: AuthorizeAttribute and User.IsInRole not working

I've been racking my brain over this for the past week, and none of the answers I've found here or elsewhere seem to be doing anything. I have an ASP.NET MVC5 application that uses SimpleMembership. I have a controller called OrganisationsController that has the following attribute:
[Authorize(Roles = "Administrator")]
I've checked the database and the user I'm logging in with is indeed in the "Administrator" role. However, neither the Authorize attribute nor User.IsInRole() return "true" for this role.
In Authorize attribute not working with roles it is suggested that
The AuthorizeAttribute calls the IsInRole method on the IPrincipal instance stored in HttpContext.User. By default IPrincipal has no roles, and in this case IsInRole will always return false. This is why access to your action is denied.
I've used the following code as suggested in that answer, but authTicket.UserData remains empty.
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
HttpCookie authCookie = Context.Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
string[] roles = authTicket.UserData.Split(',');
GenericPrincipal userPrincipal = new GenericPrincipal(new GenericIdentity(authTicket.Name), roles);
Context.User = userPrincipal;
}
}
I can't figure out what's going wrong. Why can I log in, but can't any of the roles be found?
Here's some relevant parts of the web.config:
<roleManager enabled="true" defaultProvider="SimpleRoleProvider">
<providers>
<add name="SimpleRoleProvider" type="WebMatrix.WebData.SimpleRoleProvider, WebMatrix.WebData" />
</providers>
</roleManager>
<membership defaultProvider="SimpleMembershipProvider">
<providers>
<add name="SimpleMembershipProvider" type="WebMatrix.WebData.SimpleMembershipProvider, WebMatrix.WebData" />
</providers>
</membership>
<authentication mode="Forms">
<forms loginUrl="~/Account/Login" timeout="2880" cookieless="UseCookies" />
</authentication>
and this is the InitializeSimpleMembershipAttribute I've defined:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public sealed class InitializeSimpleMembershipAttribute : ActionFilterAttribute
{
private static SimpleMembershipInitializer _initializer;
private static object _initializerLock = new object();
private static bool _isInitialized;
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// Ensure ASP.NET Simple Membership is initialized only once per app start
LazyInitializer.EnsureInitialized(ref _initializer, ref _isInitialized, ref _initializerLock);
}
private class SimpleMembershipInitializer
{
public SimpleMembershipInitializer()
{
Database.SetInitializer<UsersContext>(null);
try
{
using (var context = new UsersContext())
{
if (!context.Database.Exists())
{
// Create the SimpleMembership database without Entity Framework migration schema
((IObjectContextAdapter)context).ObjectContext.CreateDatabase();
}
}
if (!WebSecurity.Initialized)
{
WebSecurity.InitializeDatabaseConnection("VerhaalLokaalDbContext", "UserProfile", "UserId", "UserName", autoCreateTables: true);
}
}
catch (Exception ex)
{
throw new InvalidOperationException("The ASP.NET Simple Membership database could not be initialized. For more information, please see http://go.microsoft.com/fwlink/?LinkId=256588", ex);
}
}
}
}
The InitializeSimpleMembershipAttribute is only set on the AccountController.
What's exactly is going on here? Why can't the roles, which are defined and tied to users in the database, be found?
We used the System.Web.Security.Roles.GetRolesForUser(...) call and then brute force check those role arrays for an intersection. In our case this all happens inside a custom AuthorizeAttribute classes' () call. An extended attribute may not necessary for you but I wanted to give the following code snippet some context. We only used the principal to get the user name.
e.g.,
var userRoles = System.Web.Security.Roles.GetRolesForUser(username);
var allowedRoles = Roles.Split(','); // Roles is a property on the Authorize attribute
var matches = userRoles.Intersect(allowedRoles).ToArray();
if ( matches.Length > 0 ) // true if user is in an allowed role, otherwise it is not
Hope that helps! I'm sure there is a more efficient way, I just dusted off what we have been using for two years now.

asp.net mvc 2, httpmodule routedata redirect

I tried to do redirect from w/i httpmodule. I have the following code in place, and hooked up in the httpModules section. This Error event is fired as expected, but this didn't go to the /Error/Index page.
public class MyHttpModule : IHttpModule {
public void Dispose() { }
public void Init(HttpApplication context) {
context.Error += delegate {
var exception = context.Server.GetLastError();
context.Response.Clear();
context.Response.TrySkipIisCustomErrors = true;
context.Server.ClearError();
var routeData = new RouteData();
routeData.Values.Add("controller", "Error");
routeData.Values.Add("action", "Index");
IController errorController = new ErrorController();
errorController.Execute(new RequestContext(new HttpContextWrapper(HttpContext.Current), routeData));
};
}
}
Please advice,
Thanks
I created a new ASP.NET MVC 2 and ASP.NET MVC 3 application.
I added a new HttpModule and copy/pasted your code.
I registered the new HttpModule in the Web.config.
<?xml version="1.0"?>
<configuration>
<system.web>
<httpModules>
<add name="MyHttpModule"
type="MvcApplication.MyHttpModule, MvcApplication" />
</httpModules>
</system.web>
</configuration>
I throw an exception in one of the action methods of my controller, e.g.:
throw new InvalidOperationException("An exception occured.");
Whenever an unhandled exception occurs I get redirected to the Error/Index page without a problem.
Have you correctly registered the HttpModule? I only get the described behavior if the module is not registered correctly. Keep in mind that for the Visual Studio Development Web Server (Cassini) and IIS 7.X you need to register the HttpModule in different sections of the Web.config.
For IIS 7.X please use:
<?xml version="1.0"?>
<configuration>
<system.webServer>
<modules>
<add name="MyHttpModule"
type="MvcApplication.MyHttpModule, MvcApplication" />
</modules>
</system.webServer>
</configuration>

Resources