Have an MVC5 project using Windows authentication where User.Identity.Name randomly turns up empty. The site needs to display public facing and secure pages. Anonymous authentication is enabled in IIS and set to Application Pool Identity. The behavior is pretty random but most commonly repeated by navigating away from home and back again (if I sit there and click a home link it happens about 1 every 10 or so clicks) There's no special sauce in the web.config or controller action:
Web.Config
<system.web>
<authentication mode="Windows" />
</sytem.web>
HomeController
public class HomeController : BaseController
{
protected IMailer _mailer;
public HomeController(INLogger logger, IMailer mailer) : base(logger) {
this._mailer = mailer;
}
public ActionResult Index()
{
return View();
}
}
Threads out there say to use Request.SeverVariables["LOGON_USER"], but this turns up empty too.
Has me baffled, any insight would get great. Thanks!
Related
1.The Tester test my ASP.NET MVC website and report "Cookie not Sent Over SSL(4720)" issues.
2.And they provide me to solve this issue by Add <httpCookies httpOnlyCookies="true" requireSSL="true" /> in Web.config and then I followed the instructions.
3.The problem when i run and test my website the Session and TempData is null when change page. The code below is shown how i set Session and TempData.
3.1 I set Session and TempData when user go to "Home/Index".
public class HomeController : Controller
{
public ActionResult Index()
{
TempData["class"] = "A";
TempData.Keep();
Session["status"] = "NO";
return View();
}
}
3.2 When user change page to "Admin/User" i get TempData["class"] and Session["status"] but both is null.
public class AdminController : Controller
{
public ActionResult User()
{
string userclass = TempData["class"] != null ? TempData["class"].ToString() : "";
string userstatus = Session["status"] != null ? Session["status"].ToSring() : "";
UserModel usermodel = new UserModel(userclass, userstatus);
return View(usermodel);
}
}
If i delete <httpCookies httpOnlyCookies="true" requireSSL="true" /> from Web.config and test again it's work. but it's still issue "Cookie not Sent Over SSL (4720)" when tester test this website.
How to fix this problem?
P.S. Sorry for bad english skill.
If you set your cookies to be sent securely over SSL, then you must enable SSL in IIS Express for this to work.
Visual Studio configures all the necessary things (like your server certificate and the settings) when you select the SSL option for the web host.
You'll find here a full tutorial about it.
I am using MVC4, ASP.NET 4.5, C#
I want to add code to my onAuthorization method in global.asa to identify whether the connection is SSL or not, if not then to issue a permanent redirect to a SSL domain. I am using another domain that is SSLed.
In my Login controller I have code along the lines of :
[HttpGet]
public virtual ActionResult LogOn(string Error="")
{
if (Request.IsSecureConnection)
{
return View(viewModel);
}
else
{
return RedirectPermanent("https://www.mysslapp.com/logon");
}
}
I want to add this same functionality to the onAuthorization method so that when actions, covered by the [authorize] filter are called then they must also be accessed by a SSL connection. So I believe my global.asa code needs changing. However it will not accept "Request.IsSecureConnection", as the context is different.
My "pseudo" Global.asa onAuthorization routine is:
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (Request.IsSecureConnection)
{
base.OnAuthorization(filterContext);
}
else
{
RedirectPermanent("https://www.mysslapp.com/logon");
}
}
The above will not work, but it describes what I am trying to achieve. I would appreciate help on how I need to change the above code to make it work, such that any "adventurous" use of urls, on a non SSL connection will automatically redirect to the SSL site logon page.
Thanks in advance.
EDIT1
Think I have the first bit:
filterContext.HttpContext.Request.IsSecureConnection
EDIT2
if (filterContext.HttpContext.Request.IsSecureConnection)
{
base.OnAuthorization(filterContext);
}
else
{
filterContext.Result = new RedirectResult("https://www.mysslapp.com");
}
I think your main issue is that you have 2 separate concerns and you are trying to achieve both in one go. Your 2 concerns are:
Making every URL of domain A 301 redirect to the same URL on domain B
Making domain B redirect all requests to HTTPS
The first one is really easy. Create a new IIS site for domain A, install the IIS rewrite module, add this web.config to the site, and then adjust your DNS (if necessary) to make the site live.
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<httpRedirect
enabled="true"
destination="https://www.mysslapp.com$V$Q"
exactDestination="true"
httpResponseStatus="Permanent" />
<httpProtocol>
<redirectHeaders>
<!-- This is to ensure that clients don't cache the 301 itself -
this is dangerous because the 301 can't change when put in place
once it is cached -->
<add name="Cache-Control" value="no-cache"/>
</redirectHeaders>
</httpProtocol>
</system.webServer>
</configuration>
NOTE: The above configuration is for IIS 7.5. I am not sure if it will work on other versions of IIS.
Now none of the users of domain B will incur the performance hit of the redirect rule, so all is good.
For redirecting your users of domain B to HTTPS, you should not use 301. Why? Because not all browsers respond to 301.
You should also not only allow HTTPS on the domain, but allow both HTTP and HTTPS. Why? Because your users that type myssldomain.com will get an ugly error message instead of a fast redirect to you HTTPS protected site.
So the simple solution to making your whole site redirect to HTTPS is to use the RequireHttps attribute and register it as a global filter. The RequireHttpsAttribute uses a 302 redirect when a request comes in that is not secure.
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new RequireHttpsAttribute());
filters.Add(new HandleErrorAttribute());
}
}
As for the AuthorizeAttribute, you should leave that out of the equation altogether unless you need some customization that deals with authorization.
I've got an MVC4 project that I'm working on. When a user's login credentials are valid, I call FormsAuthentication.SetAuthCookie() to indicate that the user is logged in. (I have it wrapped in a class so I can mock the Interface for my unit tests.)
namespace FlashMercy.Shared.Security
{
using System;
using System.Web.Security;
public class Auth : IAuth
{
public void SetAuthCookie(string userId, bool remember)
{
FormsAuthentication.SetAuthCookie(userId, remember);
}
public void Signout()
{
FormsAuthentication.SignOut();
}
}
}
In the debugger, I can confirm that the .SetAuthCookie(userId, remember) line is executing, and userId is populated.
Then, I have a custom authorize attribute to check that the user is logged in:
namespace FlashMercy.Shared.Security
{
using System.Web.Mvc;
public class FlashMercyAuthorizeAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
filterContext.Result = new RedirectResult("/");
}
}
}
}
When I debug the application, the filterContext.HttpContext.User.Identity.IsAuthenticated is false even after I've supposedly set the auth cookie. Also, filterContext.HttpContext.User.Identity.Name is empty. I'm not sure what I'm missing here.
Update
If you care to look at the whole source, it's available on GitHub: https://github.com/quakkels/flashmercy.
Problem with your code is that you are using FormsAuthentication, but you didn't add it to web.config. Your web.config should have such section:
<system.web>
<authentication mode="Forms"></authentication>
...
</system.web>
Based on this Mode Asp.Net understand what authentication mode it should use, e.g. Forms, Windows, etc. And without settings it to Forms value - FormsAuthenticationModule just ignores .ASPXAUTH cookie from the request.
PS. I've downloaded your code, and with correct authentication section in web.config it works fine and updates HttpContext.User.Identity.IsAuthenticated to true.
The problem is that you only set the authentication cookie but do not have anything that load it.
It's forms authentication that uses that cookie. So you either have to activate forms authentication or you'll have to load it yourself.
filterContext.HttpContext.User.Identity.IsAuthenticated is false even after I've supposedly set the auth cookie.
This will always be the case if you do not redirect after SetAuthCookie(). The ASP.Net pipeline is in charge of authorizing the user (most of the time before we write code) in the AuthenticateRequest. Setting a Cookie does not update the current User.Identity, this requires code that has already been executed. Just make sure anytime you SetAuthCookie() you immediately redirect (server side is fine) to another URL (probably should anyway, its a good way to seperate logging in a user, and what they should do next SRP).
I have an MVC 4 app, using a custom HandleErrorAttribute to handle only custom exceptions. I would like to intercept the default 404 and other non-500 error pages and replace them with something more attractive. To that end, I added the following to my Web.config:
<system.web>
<customErrors mode="On" defaultRedirect="~/Error/Index" />
...
</ system.web>
I have an Error controller with an Index method and corresponding view, but still I get the default 404 error page. I have also tried setting my defaultRedirect to a static html file to no avail. I have tried adding error handling specific to 404's inside <customErrors>, and I even tried modifying the routes programattically, all with no results. What am I missing? Why is ASP ignoring my default error handling?
Note: I noticed earlier that I cannot test my CustomHandleErrorAttribute locally, even with <customErrors mode="On". It does work when I hit it on my server from my dev box though... not sure if that is related. This guy had the same problem.
This should work :
1. Web.Config
<customErrors mode="On"
defaultRedirect="~/Views/Shared/Error.cshtml">
<error statusCode="403"
redirect="~/Views/Shared/UnauthorizedAccess.cshtml" />
<error statusCode="404"
redirect="~/Views/Shared/FileNotFound.cshtml" />
</customErrors>
2. Registered HandleErrorAttribute as a global action filter in the FilterConfig class as follows
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new CustomHandleErrorAttribute());
filters.Add(new AuthorizeAttribute());
}
If that dont work then, Try to make yourself transfer the response by checking status codes like the Following in the Global.asax: at least it must work.
void Application_EndRequest(object sender, EventArgs e)
{
if (Response.StatusCode == 401)
{
Response.ClearContent();
Server.Transfer("~/Views/Shared/UnauthorizedAccess.cshtml");
}
}
I am going little off topic. I thought this is bit important to explain.
If you pay attention to the above highlighted part. I have specified the order of the Action Filter. This basically describes the order of execution of Action Filter. This is a situation when you have multiple Action Filters implemented over Controller/Action Method
This picture just indicates that let's say you have two Action Filters. OnActionExecution will start to execute on Priority and OnActionExecuted will start from bottom to Top. That means in case of OnActionExecuted Action Filter having highest order will execute first and in case of OnActionExecuting Action Filter having lowest order will execute first. Example below.
public class Filter1 : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
//Execution will start here - 1
base.OnActionExecuting(filterContext);
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
//Execution will move here - 5
base.OnActionExecuted(filterContext);
}
}
public class Filter2 : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
//Execution will move here - 2
base.OnActionExecuting(filterContext);
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
//Execution will move here - 4
base.OnActionExecuted(filterContext);
}
}
[HandleError]
public class HomeController : Controller
{
[Filter1(Order = 1)]
[Filter2(Order = 2)]
public ActionResult Index()
{
//Execution will move here - 3
ViewData["Message"] = "Welcome to ASP.NET MVC!";
return View();
}
}
You may already aware that there are different types of filters within MVC framework. They are listed below.
Authorization filters
Action filters
Response/Result filters
Exception filters
Within each filter, you can specify the Order property. This basically describes the order of execution of the Action Filters.
Back to the original Query
This works for me. This is very easy and no need to consider any change in Web.Config or Register the Action Filter in Global.asax file.
ok. So, First I am creating a simple Action Filter. This will handle Ajax and Non Ajax requests.
public class MyCustomErrorAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
filterContext.ExceptionHandled = true;
var debugModeMsg = filterContext.HttpContext.IsDebuggingEnabled
? filterContext.Exception.Message +
"\n" +
filterContext.Exception.StackTrace
: "Your error message";
//This is the case when you need to handle Ajax requests
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
filterContext.Result = new JsonResult
{
JsonRequestBehavior = JsonRequestBehavior.AllowGet,
Data = new
{
error = true,
message = debugModeMsg
}
};
}
//This is the case when you handle Non Ajax request
else
{
var routeData = new RouteData();
routeData.Values["controller"] = "Error";
routeData.Values["action"] = "Error";
routeData.DataTokens["area"] = "app";
routeData.Values["exception"] = debugModeMsg;
IController errorsController = new ErrorController();
var exception = HttpContext.Current.Server.GetLastError();
var httpException = exception as HttpException;
if (httpException != null)
{
Response.StatusCode = httpException.GetHttpCode();
switch (System.Web.HttpContext.Current.Response.StatusCode)
{
case 404:
routeData.Values["action"] = "Http404";
break;
}
}
var rc = new RequestContext
(
new HttpContextWrapper(HttpContext.Current),
routeData
);
errorsController.Execute(rc);
}
base.OnException(filterContext);
}
}
Now you can implement this Action Filter on Controller as well as on the Action only.Example:
Hope this should help you.
I want to share my knowledge after investigating this problem. Any comments that help improve my statements are welcomed.
In ASP.NET MVC, there are three layers that handle HTTP requests in the following order (response is transferred in reverse order):
IIS (HTTP Layer)
ASP.NET (Server Layer)
Controller (MVC Layer)
All of these layers have error handling, but each layer does it differently. I'll start with IIS.
IIS Layer
The simplest example of how IIS handles an error is to request a non existing .html file from your server, using the browser. The address should look like:
http://localhost:50123/this_does_not_exist.html
Notice the title of the browser tab, for example: IIS 10.0 Detailed Error - 404.0 - Not Found.
ASP.NET Layer
When IIS receives a HTTP request, if the URL ends with .aspx, it forwards it to ASP.NET since it is registered to handle this extension. The simplest example of how ASP.NET handles an error is to request a non existing .aspx file from your server, using the browser. The address should look like:
http://localhost:50123/this_does_not_exist.aspx
Notice the Version Information displayed at the bottom of the page, indicating the version of ASP.NET.
The customErrors tag was originally created for ASP.NET. It has effect only when the response is created by ASP.NET internal code. This means that it does not affect responses created from application code. In addition, if the response returned by ASP.NET has no content and has an error status code (4xx or 5xx), then IIS will replace the response according to the status code. I'll provide some examples.
If the Page_Load method contains Response.StatusCode = 404, then the content is displayed normally. If additional code Response.SuppressContent = true is added, then IIS intervenes and handles 404 error in the same way as when requesting "this_does_not_exist.html". An ASP.NET response with no content and status code 2xx is not affected.
When ASP.NET is unable to complete the request using application code, it will handle it using internal code. See the following examples.
If an URL cannot be resolved, ASP.NET generates a response by itself. By default, it creates a 404 response with HTML body containing details about the problem. The customErrors can be used to create a 302 (Redirect) response instead. However, accessing a valid URL that returns 404 response from application code does not trigger the redirect specified by customErrors.
The same happens when ASP.NET catches an exception from application code. By default, it creates a 500 response with HTML body containing details about the source code that caused the exception. Again, the customErrors can be used to generate a 302 (Redirect) response instead. However, creating a 500 response from application code does not trigger the redirect specified by customErrors.
The defaultRedirect and error tags are pretty straight-forth to understand considering what I just said. The error tag is used to specify a redirect for a specific status code. If there is no corresponding error tag, then the defaultRedirect will be used. The redirect URL can point to anything that the server can handle, including controller action.
MVC Layer
With ASP.NET MVC things get more complicated. Firstly, there may be two "Web.config" files, one in the root and one in the Views folder. I want to note that the default "Web.config" from Views does two things of interest to this thread:
It disables handling URLs to .cshtml files (webpages:Enabled set to false)
It prevents direct access to any content inside the Views folder (BlockViewHandler)
In the case of ASP.NET MVC, the HandleErrorAttribute may be added to GlobalFilters, which also takes into account the value of mode attribute of the customErrors tag from the root "Web.config". More specifically, when the setting is On, it enables error handling at MVC Layer for uncaught exceptions in controller/action code. Rather than forwarding them to ASP.NET, it renders Views/Shared/Error.cshtml by default. This can be changed by setting the View property of HandleErrorAttribute.
Error handling at MVC Layer starts after the controller/action is resolved, based on the Request URL. For example, a request that doesn't fulfill the action's parameters is handled at MVC Layer. However, if a POST request has no matching controller/action that can handle POST, then the error is handled at ASP.NET Layer.
I have used ASP.NET MVC 5 for testing. There seems to be no difference between IIS and IIS Express regarding error handling.
Answer
The only reason I could think of why customErrors is not considered is because they are created with HttpStatusCodeResponse from application code. In this case, the response is not altered by ASP.NET or IIS. At this point configuring an alternative page is pointless. Here is an example code that reproduces this behavior:
public ActionResult Unhandled404Error()
{
return new HttpStatusCodeResult(HttpStatusCode.NotFound);
}
In such scenario, I recommend implementing an ActionFilterAttribute that will override OnResultExecuted and do something like the following:
int statusCode = filterContext.HttpContext.Response.StatusCode;
if(statusCode >= 400)
{
filterContext.HttpContext.Response.Clear();
filterContext.HttpContext.Response.Redirect("/Home/Index");
}
The implemented ActionFilterAttribute should be added to GlobalFilters.
Create a Controller ErrorController.
public class ErrorController : Controller
{
//
// GET: /Error/
public ActionResult Index()
{
return View();
}
}
Create the Index view for the action.
in Web.config
<customErrors mode="On">
<error statusCode="404" redirect="Error/Index"/>
</customErrors>
When you are handling errors in your code/logic
[HandleError]
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "Modify this template to jump-start application.";
return View("Index2");
}
}
[HandleError] attribute - will redirected to the Error.cshtml page inside shared folder.
I am not sure this answer will help you but this a simple way... I placed error.html in / and turned mode to on for custom errors in web config and this works perfectly...
<system.web>
<customErrors defaultRedirect="~/Error.html" mode="On" />
</system.web>
this error.html is a basic html page with head and body..
To me, it works deleting the default Error.cshtml file, now it is taking the custom Error defaultRedirect page in Web.config.
Greetings,
in my asp.net mvc application what i would like to do is to enable access to some pages only after user was successfully authorized. I have already created custom membership provider and that works fine. How can I, in web config create such rule - for instance for all pages in ~Admin/ folder? I don't want to create on every controller's action the validation code.
For now i have in my web.config the following statement:
<location path="~/Admin">
<system.web>
<authorization>
<deny users="?"/>
</authorization>
</system.web>
but it doesn't work.
Doing authorization logic in config files has one big disadvantage: it cannot be easily unit tested, and something so important as authentication should be unit tested. I would recommend you for this matter to write a custom authorization filter which could be used to decorate a base controller for all admin actions that requires authentication:
[AttributeUsage(
AttributeTargets.Method | AttributeTargets.Class,
Inherited = true
)]
public class RequiresAuthenticationAttribute
: FilterAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
filterContext.Result = new RedirectResult(
string.Format("{0}?ReturnUrl={1}",
FormsAuthentication.LoginUrl,
filterContext.HttpContext.Request.Url.AbsoluteUri
)
);
}
}
}
And your admin controller:
[RequiresAuthentication]
public class AdminController : Controller
{
// .. some actions that require authorized access
}