I'm using ASP.NET MVC 4 to build a simple intranet web app. I've put in place a login system and configure the RouteConfig.cs to display the login view as the "home page".
My question is simple : I've other views that "visitors" can't access if they're not logged. To perform tests, I try to access to these pages directly via the url and it works. How can I secure this by blocking the access to all the pages (excepted the login page)?
I've read things about the Web.config and authorizations but nothing worked. Any help guys?
EDIT : As Khanh TO told, working with the AutorizeAttribute is the best way to do it. However, I still have a little issue. When I'm logged as a simple user, I should not be able to see the admin pages (and that's the case) but when I attempt to do it, it redirects me to the login page (and I'm already logged !). I think this is because of the code below. The thing is, I would like to redirect to the home page if a user tries to attempt an unauthorized page. Possible to do that?
Here is the code I was speaking about :
<authentication mode="Forms">
<forms loginUrl="~/User/Login" timeout="2880" />
</authentication>
Also, I wanted to precise that my users are stored into a simple table in which the role is defined by a bool type (isAdmin to know if a user is an admin or not).
If you need to selectively apply authorization for some pages, use AuthorizeAttribute on controller or action method:
Sample code:
[Authorize]
public class SecuredController : Controller
{
public ActionResult Index()
{
return View();
}
}
Or:
public class SecuredController : Controller
{
[Authorize]
public ActionResult Secure()
{
return View();
}
public ActionResult NonSecure()
{
return View();
}
}
Quoted from MSDN:
When you mark an action method with AuthorizeAttribute, access to that
action method is restricted to users who are both authenticated and
authorized. If you mark a controller with the attribute, all action
methods in the controller are restricted.
The Authorize attribute lets you indicate that authorization is restricted to predefined roles or to individual users. This gives you a high degree of control over who
is authorized to view any page on the site.
If an unauthorized user
tries to access a method that is marked with the Authorize attribute,
the MVC framework returns a 401 HTTP status code. If the site is
configured to use ASP.NET forms authentication, the 401 status code
causes the browser to redirect the user to the login page.
If you need to apply the authorize attribute globally except for the login page. You can add the AuthorizeAttribute to the global filter like this:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new AuthorizeAttribute());
}
And apply AllowAnonymousAttribute to your login view:
public class AccountController : Controller
{
[AllowAnonymous]
public ActionResult Login()
{
return View();
}
}
In the web.config, enable Form Authentication:
<authentication mode="Forms">
<forms loginUrl="~/Account/Login" timeout="2880" />
</authentication>
Related
currently I have below code implemented which is redirecting to login upon timeout.
<authentication mode="Forms">
<forms loginUrl="~/Login/Index" timeout="45" />
</authentication>
But now I have to redirect to different urls based on user type.
In your Index action in AccountController (or whichever controller is used to call the required view) you can check the user's type and redirect from there.
For example
public ActionResult Index()
{
if(usertype=="admin")
{
RedirectToAction("Theactionyouwanttoredirectto");
}
}
How you get usertype of course depends on what you mean by "user type" and how your application is structured.
Iam using authorize to lock the controller after a user signs in. Users are saved in EF with no use of roles or something like that. I read the documentation of authorize and it seems like I have to specify users or roles that could have access to the locked controller;
Here i have the Account controller;
[Authorize]
public ActionResult Index()
{
return View();
}
When a user logs in, the user should be redirected to this action which is locked with the attribute [Authorize], but unfortunately i am getting this error "Html error 401 unauthorized".
My question is: how to give access to all users that are registered in EF DB to get redirected to the next page and skip this error??
Following are the solution:
1- I had to change the Web.config file and add in the following:
<authentication mode="Forms">
<forms loginUrl="~/Home/Login" timeout="2880" />
</authentication>
and then decorate the controller with [Authorize]
2- I had to clear cookies from browser (Google Chrome) to make it work otherwise Iam still be able to get access to account controller. Cleaning cookies will delete the old one and start a new one.
Adding the Authorize attribute with no parameters will grant access to any user who is logged in:
[Authorize]
public class PrivateAreaController
{
//
}
Or on an individual action:
public class MostlyPublicController
{
[Authorize]
public ActionResult GetPrivateStuff()
{
//
}
}
I'm creating ASP.NET MVC 5 with "Individual User Accounts" Authentication Template.
Then I create custom login page to authenticate user from database with Forms Authentication.
<authentication mode="Forms">
<forms loginUrl="~/User/SignIn" timeout="2880" protection="All" />
</authentication>
For testing the custom login page, I add HomeController with Authorize attribute.
After run the visual studio, it redirect to the SignIn page
The problem is when I remove Authorize attribute at HomeController and add authorize filter at FilterConfig.cs
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
filters.Add(new AuthorizeAttribute());
}
I've got HTTP error: "HTTP Error 401.0 - Unauthorized". How to fix this?
By doing this you place autherization restrictions on the login and register actions.
So basicly you need to be autherized to login or register.
Placing the allowAnonymous attributes on these actions might solve the problem.
Because i put the correct answer as a comment at least 8 mins before anyone did I'll add it as an answer.
You basically need to add the [AllowAnonymous] attribute to your SignIn action, because essentially the filter you have created is making every action require authorisation to 'view'.
Taken from AllowAnonymous attribute information
"...in ASP.NET MVC 4, it's baked in. By baked in, I mean that: There's a built-in AllowAnonymousAttribute in the the System.Web.Mvc namespace which whitelists actions for anonymous access."
Taken from Global action filters information
"ASP.NET MVC 3.0 introduces global action filters - an easy way to apply an action filter to every action in an MVC application."
[AllowAnonymous]
public ActionResult SignIn() {
}
You've added a global attribute. You need to append an attribute [AllowAnonymous] to the ones you want to not have the rule applied;
[AllowAnonymous]
public ActionResult Home() {
// ...
}
Becasue you add AuthorizeAttribute to global filter. When you no authorized and access all action will redirect to "~/User/SignIn", But the action mapping "~/User/SignIn" also need you authorized. You can try this:
public class NoNeedAuthorizeAttribute : Attribute
{
}
And inherit AuthorizeAttribute override OnAuthorization method, If a action has NoNeedAuthorize attribute then ignore:
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (!filterContext.ActionDescriptor.GetCustomAttributes(typeof(NoNeedAuthorizeAttribute), true).Any())
{
base.OnAuthorization(filterContext);
}
}
Add the NoNeedAuthorizeAttribute to your SignIn action:
public class UserController : Controller
{
[NoNeedAuthorize]
public ActionResult SignIn()
{
//Sign in code..
}
}
Finally, add 'GlobalAuthorizeAttribute' to globle filter:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
filters.Add(new GlobalAuthorizeAttribute());
}
I have an internal web app being built in ASP.NET 4. We are stuck with using an authentication API built by another team. If a user to the site is authenticated successfully for the site I would like to give them access to the entire site.
In ASP.NET WebForm days I just used to keep a custom User object in session. If that object was null I knew the user wasn't authenticated. Is there a similar but improved method for this in MVC. I don't want to have to build my own provider of the ASP.NET Membership model if possible. What is the simplest way of doing this?
You can use Forms Authentication in conjuction with Authorize attibute as follows,
To restrict access to a view :
Add the AuthorizeAttribute attribute to the action method declaration, as shown below,
[Authorize]
public ActionResult Index()
{
return View();
}
Configuring Forms Authentication in web.config
<authentication mode="Forms">
<forms loginUrl="~/Account/Login" timeout="2880" />
</authentication>
Login Post Action:
Set Authentication cookie if user is valid
[HttpPost]
public ActionResult Login(User model, string returnUrl)
{
//Validation code
if (userValid)
{
FormsAuthentication.SetAuthCookie(username, false);
}
}
Log off Action:
public ActionResult LogOff()
{
FormsAuthentication.SignOut();
return RedirectToAction("Index", "Home");
}
You probably want to have a custom authorization filter. Here's an example: Custom filters in MVC. You can then apply this filter globally on app start (using RegisterGlobalFilters).
public class LegacyAuthorize : AuthorizeAttribute
{
public override void OnAuthorization(HttpActionContext actionContext)
{
if (HttpContext.Current.Session["User"] == null)
base.HandleUnauthorizedRequest(actionContext);
}
}
Then in your global.asax you'd have something like this:
GlobalFilters.Filters.Add(new LegacyAuthorize());
You can try with something like this:
FormsAuthentication.SetAuthCookie(username, rememberMe);
to set the cookie for authenticated user, then just use the [Authorize] attribute on the Controller or Action that need authentication.
Try googling on the subject for further info, you will find a lot of stuff on authentication and authorization in MVC.
Everything you could do in forms you can do in MVC, just set the session variable in the controller login action.
Or you can do this:
In the login action add formsauthentication.setauthcookie("username")
After this any action with the [Authorize] keyword will allow the current user in.
You can do the Session Authentication by simply putting a session variable value when the login is successful. Eg
public ActionResult Index(Models.Login login)
{
if (ModelState.IsValid)
{
Dal.Login dLogin = new Dal.Login();
string result = dLogin.LoginUser(login);
if (result == "Success")
Session["AuthState"] = "Authenticated";
}
return View();
}
Now the trick is that you should have a common layout page of all the views to which you have to check for authentication. And in this layout page just do a razor check like this -
<body>
#if (Session["AuthState"] != "Authenticated")
{
Response.Redirect("~/login");
}
// other html
</body>
I have been using this method in my application admin panel.
I have an MVC application with the following block inside in Web.config:
<authentication mode="Forms">
<forms loginUrl="~/Login" timeout="2880" />
</authentication>
So, if a user requests a page and authorization fails, they will be redirected to ~/Login.
That's fine, and I need it for most of my controllers. However, I have a controller which I'd like to bypass this rule with. How can I allow specific controllers to ignore this rule?
My problem is that in my MVC application (which has several controllers), I have a certain controller which hosts a REST interface (not meant for browser use). Since this controller isn't meant for browser-consumption, I don't want it sending back an entire login page, (or any page whatsoever actually, just strings or partial views.)
Note that I'm using custom [Authorize...] attributes on my actions, and when THESE fail, they redirect to an Error action--but, unfortunately, my Error action (which returns a short string) is being redirected to the Login page because of this configuration setting!
I'm getting dizzy trying to figure this out, what am I doing wrong? I can provide more details if necessary.
You could extend the AuthorizeAttribute class and override HandleUnauthorizedRequest, you may want to return a Forbidden http status code rather than a custom message.
public class CustomAuthorizationAttribute : AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
// You need to set this action result to something other than a HttpUnauthorizedResult,
// this result will cause the redirection to the login page
// Forbidden request... does not redirect to login page
// filterContext.Result = new HttpStatusCodeResult(403);
filterContext.Result = new ErrorActionResult { ErrorMessage = "Unauthorized Access" };
}
}
public class ErrorActionResult : ActionResult
{
public string ErrorMessage { get; set; }
public override void ExecuteResult(ControllerContext context)
{
context.HttpContext.Response.Write(this.ErrorMessage);
}
}
Add the following after your system.web element in Web.config:
<location path="home">
<system.web>
<authorization>
<allow users="*" />
</authorization>
</system.web>
</location>
This will allow unauthenticated users access to "/home" and thus any actions on the HomeController.
Phil Haack has a pretty thorough blog post on this exact subject: Prevent Forms Authentication Login Page Redirect When You Don’t Want It