Asp.Net MVC 4 User is authenticated even if not authorized - asp.net-mvc

I am working in my first MVC project and i am having troubles with authentication.
I have a login page that correctly validates the user by my active directory. But, even authenticated not all users are authorized to access the system, so I use a section in web.config to check if user have permissions. Something like:
<authorization>
<allow users="john,mary,paul,bill,jane,anna" />
<deny users="*" />
</authorization>
It works fine and the user always is redirected to login if doesn't have permission.
BUT, when I check if user is Authenticated, the result is always true. And, in login page, I want to check if I must show a message to logged AND authorized users. Something like:
#if (User.Identity.IsAuthenticated && User.Identity.IsAuthorized)
{
#Html.Partial("_Menu");
}
So... How I do it?

Authentication an Authorization are 2 different concepts. Authentication means you know who the person is. Authorization means they have specific permissions.
If you want to check if they are authorized to perform some action and provide them a button or link to perform that action (or access some data, whatever it may be), then you'll have to check if they have the permissions using other means. Some more specifics about the setup you have would help answer the question better.

To Authenticate, check on your web.config if you have this tag:
<authentication mode="Forms">//It cannot be "None"
<forms loginUrl="~/MyLoginURL" timeout="2880" />
</authentication>
And in your LoginMethod, that authenticate the user, make sure you called any .NET method that authenticate the user on its Identity, example:
using System.Web.Security;
FormsAuthentication.SetAuthCookie(login, boolean to cookie) // Or others FormsAuthentication methods that authenticate.
There is an AuthorizeAttribute in System.Web.Mvc, that can be used with your actions or controllers, like:
[Authorize]
public ActionResult Index()
{ ... }
or
[Authorize]
public class HomeController
{ ... }
This AuthorizeAttribute will check if the current user is Authorized. You can create your own attribute by just inheriting the AuthorizeAttribute and override OnAuthorization and AuthorizeCore

Once you perform a successful login (FormsAuthentication.SetAuthCookie), your user get authenticated so until you dont explicitly logoff (FormsAuthentication.SignOut) that's the correct behaviour, as said authentication and authorization are two different things.
I guess you want to show a menu only to users that have been authenticated through AD, in this case you can just restrict your resources with:
#if (User.Identity.IsAuthenticated)
{
#Html.Partial("_Menu");
}
If authorized, a user is also authenticated so no need to do the double check. You can then use the [Authorize] attribute, which relies on User.Identity.IsAuthorized.
Authorization comes handy when you can use Roles to group your users. Check if your ADusers belong to any group, if so you could do something like:
<authorization>
<allow users="*" allow role="yourdomain\yourgroup" />
<deny users="*" />
</authorization>

Related

Allow a few routes and block all other ASP.NETMVC

I have an application in ASP.NET MVC that we bought that has multiples possible routes and that might get even bigger because we can dynamically add pluggins. We're talking hundreds of different pages and controllers. We modified/added the ones that we needed (mostly reskinning). And now we have to make sure that only the pages that we modified are accessible to the user. Someone that knows the application that we bought could go to routes that we don't want him to see.
So in the end, my question is :
Is there a way to deny access to all the routes except the ones that I want.
Since the application is HUGE. I can't/don't want to go to individual controller and add a redirect or a Data annotation. Also, I dont want to list every url I deny because that would also be crazy long and would not work if we add a plugin later on. I have like 300 url to deny, 20 to allow..
I'm thinking something like this in the webconfig ??
deny *
permit mycontroller/action1
permit mycontroller/action2
I should also add, that this application does not use any kind of authentication/authorization. I want any user to be able to use the application.
We're hosting it in Azure wep app, would that be a solution ?
If anyone is stuck with the same problem, here's how I it.
It's all done in the web.config of the application:
You start by denying everything with this
<authorization>
<deny users="?" />
</authorization>
<authentication mode="None" />
Then, you can whitelist the URLs that you want like this:
<location path="mycontroller/action1">
<system.web>
<authorization>
<allow users="?" />
</authorization>
</system.web>
<location path="mycontroller/action2">
<system.web>
<authorization>
<allow users="?" />
</authorization>
</system.web>
So every URL dans does not have a location tag is considered not valid and will raise a 401 exception. You can then do what you want from there.
Hope this helps someone :)
use the [Authorize] attribute at the controller level and then [AllowAnonymous] for the action
[Authorize]
Public class mycontroller : Controller
{
Public ActionResult action1 ()
{
Return View();
}
[AllowAnonymous]
Public ActionResult action2 ()
{
Return View ();
}
}

Register page redirects to login page asp.net mvc

I have this site hosted in a couple of places.
http://web83.jet.studiocoast.com.au/Account/Register
http://flowerpictures.tesselaars.com/Account/Register
Both with the same code.
In a weird way http://flowerpictures.tesselaars.com/Account/Register redirect it to login.
The register action is as simple as
//
// GET: /Account/Register
public ActionResult Register()
{
return View();
}
Not sure if this might help but the register view uses
#Membership.MinRequiredPasswordLength
What works in one location doesn't in another. Both sit on the same server.
Looks like you're restricting anonymous users from accessing this action. This'll happen when you've set the Authorize attribute, at the class level - thus cascading accessibility down to your action methods.
Remove the Authorize attribute that's at the class level, and only add it to applicable action methods inside the class.
It'd also be good to look at the web.config and check the Authorize section where the accessibility is configured for anonymous and authenticated users:
<configuration>
<system.web>
<authentication mode="Forms"/>
<authorization>
<deny users="?"/>
</authorization>
</system.web>
<location path="/account/register">
<system.web>
<authorization>
<allow users="*"/>
</authorization>
</system.web>
</location>
</configuration>

Why is AllowAnonymous not working while deployed to Azure Websites?

I have a MVC4 web app with the following controller
[Authorize]
public class AccountController : BaseController
{
[AllowAnonymous]
public ActionResult SignInRegister(LoginModel loginModel, string returnUrl)
{
//some implementation
}
//other secured actions
}
This is working as expected when running locally, but as soon as I deploy it to the Free Azure Website I get a 401 error code with the message: You do not have permission to view this directory or page.
Removing the [Authorize] attribute and redeploying works as expected, adding it again and redeploying brings back the problem.
I even tried the fully qualified class names: System.Web.Mvc.Authorize and System.Web.Mvc.AllowAnonymous with the same results.
The app is using .NET 4.5 and the Azure Website is also configured to use 4.5.
UPDATE:
The BaseController has an action that returns the Header as partial view which was not decorated with [AllowAnonymous]. Locally it resulted in the page being displayed without the header, but on Azure Websites the response was cut off and only returned with the error message mentioned above. I had not realized the header was missing until I purposely looked into it.
Now the question begs to be asked: why is Azure Websites overriding the response?
The BaseController has an action that returns the Header as partial view which was not decorated with [AllowAnonymous]. Locally it resulted in the page being displayed without the header, but on Azure Websites the response was cut off and only returned with the error message mentioned above. I had not realized the header was missing until I purposely looked into it.
Now the question begs to be asked: why is Azure Websites overriding the response?
I had the exact same problem and like Jonas' update says, you need to look out for Actions that return Partial Views AND have the [Authorize] attribute.
What you need to do is to remove the [Authorize] attribute and then if your action needs the user to be authenticated to render properly, have your code handle the unauthorized case.
Example is if your page displays the currently logged in user's name via a Partial. Have your action display an empty string or something else if the currently logged in user is not available.
Check your web.config if you have
<authorization>
<deny users="?" />
</authorization>
its override [AllowAnonymous]
Add to web.config section:
<location path="YourController/AnonymousMethod">
<system.web>
<authorization>
<allow users="*"/>
</authorization>
</system.web>
</location>
to allow anonymous access for AnonymousMethod

ASP.NET MVC Forms authentication and unauthenticated controller actions

I have a ASP.NET MVC site that is locked down using Forms Authentication. The web.config has
<authentication mode="Forms">
<forms defaultUrl="~/Account/LogOn" loginUrl="~/Account/LogOn" timeout="2880"/>
</authentication>
<authorization>
<deny users="?"/>
</authorization>
None of my pages other than Account/LogOn can be viewed unless the user is authenticated.
Now I am trying to add PayPal IPN to my site and in order to do that I need to have two pages that handle PayPal's payment confirmation and thank you page. These two pages need to be available for anonymous users.
I would like these pages to be controller actions off my Account controller. Is there any way I can apply an attribute to specific action methods that make them available to anonymous users? I found a several posts here that attempt to do that but there was most people wanted the opposite scenario.
Basically I want may AccountController class to have no authorization for most of the methods except for a few. Right now it looks like only the LogOn method is available to anonymous users.
Yes you can. In your AccountController there's an [Authorize]-attribute either on class-level (to make the whole controller restricted) or on specific methods.
To make specific actions restricted you simply use the Authorize-attribute on the methods that handle these actions, and leave the controller-class unrestricted.
Here are a few examples... hope it helps
To require users to login, use:
[Authorize]
public class SomeController : Controller
// Or
[Authorize]
public ActionResult SomeAction()
To restrict access for specific roles, use:
[Authorize(Roles = "Admin, User")]
public class SomeController : Controller
// Or
[Authorize(Roles = "Admin, User")]
public ActionResult SomeAction()
And to restrict access for specific users, use:
[Authorize(Users = "Charles, Linus")]
public class SomeController : Controller
// Or
[Authorize(Users = "Charles, Linus")]
public ActionResult SomeAction()
As you can see, you can either use the attribute at class-level or at method-level. Your choice!
I don't think there is an "Unauthorize" attribute that can be applied to actions and if you don't want to place "[Authorize]" on all but two actions in a controller try the following:
Here are two methods I can think of:
1- Location attribute in Web.config (Not sure if this will work with MVC routing etc.)
After your
<system.web> stuff </system.web>
in web.config file, add the following:
<location path="Account/ActionOne">
<system.web>
<authorization>
<allow users ="*" />
</authorization>
</system.web>
</location>
Where Account/ActionOne is the name of the action method you want to give anonymous access to. For the second Action, copy the above code and paste it right after it and change the name of the Action.
I'm not sure if this will work because of MVC routing etc, but give it a try.
2- Base Controller
If the previous solution didn't work, your best bet would be to create a base controller that is decorated with the Authorize attribute:
[Authorize]
public class AuthorizeControllerBase : Controller {}
Then have all your controllers inherit from it:
public class AccountController : AuthorizeControllerBase
{
// your actions etc.
}
This will make any controller that inherits from AuthorizeControllerBase require authorization/logging in to invoke any methods.
Then you would need to remove from your web.config
Instead of securing all resources on your website by default and then looking for a way to provide anonymous access for individual resources, you're probably better off taking the opposite approach. Don't specify authorization rules in your web.config, then use Authorization filters (see Mickel's answer) to secure individual controllers and/or actions.

Problem with Ajax Call to Action Method on Controller Requiring Authenticated User

Using a technique I found in one of the recent ASP.NET MVC books, I have an action method on a controller that returns a partial view for an ajax request and a full view actionresult for a normal get request -- it checks the IsAjaxRequest property of the Request object to determine which type of actionresult to return. The partial view returned by the action method returns the HTML to display a table of records from a database. The controller that contains the action method is tagged with the Authorize attribute so that only logged in users can call the controller's methods. I am using forms authentication with a 30-minute timeout and sliding expiration.
The problem arises after the user's 30-minute timeout has been reached. Because the controller is marked with the Authorize attribute, a call to the action method after the expiration timeout redirects the user to the login page. However, because this is an ajax call, the html for my login page is returned and rendered in the middle of the page that should contain the HTML table of records that would normally be returned by the action method in the partial view. The ajax call is not really failing, just returning the html for the wrong page.
Has anyone encountered and dealt with this problem? I am trying to avoid having to move all of my server side code that handles ajax calls into a separate controller that does not require an authenticated user, but that seems like my only alternative at this point. Even that won't produce the behavior I would expect, because it will allow the user to continue using the web page even after the 30 minute timeout has been reached -- it will not redirect to the login page.
Thanks for any advice.
Edit
The solution below with the custom AuthorizeAttribute would seem to have me heading in the right direction, but I cannot even get to this code. It appears that the code in the custom AuthorizeAttribute is never reached after the expiration timeout is reached. It seems like forms authentication is causing a redirect to the login page long before the attribute code. The custom AuthorizeAttribute is the only one on my controller. I also have the following web.config values (the timeout value is set extremely low to trigger a timeout for testing):
<authentication mode="Forms">
<forms loginUrl="~/Account/Login" timeout="1" slidingExpiration="true" defaultUrl="~/ErrorReport/Index" requireSSL="true" protection="All"/>
</authentication>
<authorization>
<deny users="?"/>
<allow users="*"/>
</authorization>
<location path="Content">
<system.web>
<authorization>
<allow users="*"/>
</authorization>
</system.web>
</location>
<location path="Scripts">
<system.web>
<authorization>
<allow users="*"/>
</authorization>
</system.web>
</location>
Are the authorization web.config elements getting in the way? Should I not be using them with ASP.NET MVC?
I actually have a blog post queued up about this that I'll add to the comment when it has been posted, but in the time being here's what's happening.
At a high level, when the ASP.NET runtime sees a 401 response code it will automatically convert it to a redirect and send the user to the login page. What you want to do is bypass that 401.
In MVC, when you're using the AuthorizeAttribute, it checks to see if the user is authorized and, if not, returns an HttpUnauthorizedResult. This basically forces the runtime to redirect the user to the login page. What you want to do is override this behavior.
To do this, extend the AuthorizeAttribute as such:
public class AuthorizeWithAjaxAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
if (filterContext.Result is HttpUnauthorizedResult && filterContext.HttpContext.Request.IsAjaxRequest())
{
filterContext.HttpContext.Response.StatusCode = 200;
filterContext.Result = /* Some result recognized by the client */
}
}
}
When you make an AJAX request and the data of the response is equal to what you return as the filter context's result, simply redirect the user to the login page via javascript.
Yes, this problem is fairly common. It also has a simple solution - in the part of the Javascript which is handling the AJAX response, check for a timeout before using the response. If the timeout has occurred, redirect the user to the login page.
Alternately, if the server-side script is itself checking for a session timeout (which appears to be your case), it becomes even simpler. Do not return an entire login page, instead just return a flag like 'need_login'. Further, simply write the Javascript handler to check if the returned value is 'need_login' and load the login page if true.

Resources