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.
Related
I have created a ASP.NET MVC core application. I just finished creating and logging in a user. Now I'm trying to figure out
1. how to have the user login info saved
2. how to require a user to be logged in to visit the rest of the the site.
For saving the user log info. I was going to use either Identity or Asp.Net.Session, but seeing has I cant find anything that shows how to require the user to be logged in to visit routes, I’m unsure which option would work best.
Could someone explain to me how to require authentication before accessing a route, and which of the saving login info works best with that method?
Current Login Example
public ActionResult Login(LoginModel model){
if (ModelState.IsValid){
SqlConnect test = new SqlConnect("Data Source=/home/andrew/database.db","Accounts");
var check = test.count(model.UserName,model.Password);
if(check){
// Found in database
return RedirectToAction("Index","Home");
}
}
// If we got this far, something failed, redisplay form
return RedirectToAction("Error","Home");
}
You shouldn't secure routes. When using placeholders (such as in the default route), it is possible that more than one route can reach an action method. These alternate paths are sure to be difficult (nearly impossible) to keep secure as your application changes over time.
So the best option is to secure the resources by preventing them from being served. Note that this is also the way security works in ASP.NET, so both ASP.NET and MVC hook into the same mechanism (IPrincipal and IIdentity) to control both authentication and authorization. ASP.NET Identity and older security frameworks from Microsoft all use this extension point to plug into MVC and ASP.NET.
In MVC, it is the AuthorizeAttribute that uses these interfaces to secure action methods. AuthorizeAttribute can be used in several ways:
On Action Methods
public class MyController
{
[Authorize(Roles = "Admin,SuperUser")]
public ActionResult Index()
{
return View();
{
}
On Controllers
[Authorize(Roles = "Admin,SuperUser")]
public class MyController
{
[AllowAnonymous] // All users have access
public ActionResult Index()
{
return View();
{
// Only authenticated users in Admin or SuperUser
// role have access
public ActionResult Contact()
{
return View();
{
// Only authenticated users in Admin or SuperUser
// role have access
public ActionResult About()
{
return View();
{
}
Globally
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new AuthorizeAttribute());
filters.Add(new HandleErrorAttribute());
}
}
This means that only authenticated users can access any action method in your application. You can also use roles here like filters.Add(new AuthorizeAttribute() { Roles = "Foo,Bar" });. And you can allow specific controllers/actions access to unauthenticated users with the AllowAnonymousAttribute.
You can also subclass AuthorizeAttribute if you have more complex business logic than just Users and Roles.
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 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>
Assume like this is my SampleController action method
public ActionResult AdminView()
{
return View()
}
If want this controller method to be called if the logged in user belongs to admin role, otherwise this method call should be blocked and the user should get an some custom unauthorized access error page.
In my asp .net mvc web application, when the user logs in, I am storing the user role in a session as a string. And whenever there is a need to validate the user role, I compare the value stored in the session against a constant say "ADMIN_ROLE". But I am writing that piece of code to check for the user role in almost every controller action method and then either return an appropriate view for the user or an unauthorized access page view if the user role is restricted for the controller action method.
I googled and read that we can use something like this.
[Authorize(Roles="admin")]
public ActionResult AdminView()
{
return View()
}
But I am not sure how the Authorize and the Roles keyword works. How when putting Roles = "Admin", is going to help in checking my user role string stored in the session, or how I can redirect a user to unauthorized page, in case the role does not match the role tagged for the action method.
As per my thinking you need to code for authorization.
public class AuthorizeAttribute : FilterAttribute, IAuthorizationFilter
{
private readonly RoleEnum[] _acceptedRoles;
public AuthorizeAttribute(params RoleEnum[] acceptedroles)
{
_acceptedRoles = acceptedroles;
}
public AuthorizeAttribute(params bool[] allowAll)
{
if (allowAll[0])
_acceptedRoles = new RoleEnum[] { RoleEnum.Admin, RoleEnum.user};
}
public void OnAuthorization(AuthorizationContext filterContext)
{
if (SessionHelper.UserInSession == null)//user not logged in
{
FormsAuthentication.SignOut();
filterContext.Result =
new RedirectToRouteResult(new System.Web.Routing.RouteValueDictionary {{ "controller", "Home" },
{ "action", "Index" },
{ "returnUrl", filterContext.HttpContext.Request.RawUrl } });//send the user to login page with return url
return;
}
if (!_acceptedRoles.Any(acceptedRole => SessionHelper.UserInSession.UserRoles.Any(currentRole => acceptedRole == currentRole.Role)))
//allow if any of the user roles is among accepted roles. Else redirect to login page
throw new UnauthorizedAccessException();
}
}
This is also work for return URL.
As per comments, if you are using custom authentication/authorization mechanism then you need to implement custom authorize attribute where you can put custom logic to check if user has admin role or not. Something like below:
public class CustomizedAuthorizeAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
//check for role in session variable "ADMIN_ROLE"
//if not valid user then set
filterContext.Result = new RedirectResult(URL)
}
}
There is a small nice explanation in this link:
http://weblogs.asp.net/jgalloway/archive/2011/04/28/looking-at-how-asp-net-mvc-authorize-interacts-with-asp-net-forms-authorization.aspx
as per this:
ASP.NET MVC includes an [Authorize] attribute, which when placed on any controller actions will forbid unauthorized access. The AuthorizeAttribute allows you to specify a list of roles or users.
You can also place the AuthorizeAttribute on a controller, in which case it will apply to all actions in the controller. Attempting to access an action secured by the AuthorizeAttribute when you're not logged in will take you to a standard LogOn screen, with a link to register if you don't already have an account.
How does the [Authorize] attribute redirect me to Log On?
The AuthorizeAttribute is an ActionFilter, which means that it can execute before the associated controller action. The AuthorizeAttribute performs its main work in the OnAuthorization method, which is a standard method defined in the IAuthorizationFilter interface. Checking the MVC source code, we can see that the underlying security check is really just looking at the underlying authentication information held by the ASP.NET context:
IPrincipal user = httpContext.User;
if (!user.Identity.IsAuthenticated)
{
return false;
}
If the user fails authentication, an HttpUnauthorizedResult ActionResult is returned, which produces an HTTP 401(Unauthorized) status code. If it weren’t for ASP.NET Forms Authentication, an HTTP 401 status code would be sent to the browser, which would show the browser’s default login prompt.
I'm looking for a tutorial or small introductory guide on creating a login system for ASP.Net MVC3 without using the default ASP.Net membership provider.
I've created a new MVC3 project with the Internet Application template, and here is the contents of _LogOnPartial.cshtml:
#if(Request.IsAuthenticated) {
<text>Welcome <strong>#User.Identity.Name</strong>!
[ #Html.ActionLink("Log Off", "LogOff", "Account") ]</text>
}
else {
#:[ #Html.ActionLink("Log On", "LogOn", "Account") ]
}
Is #User.Identity.Name part of the membership provider? I'd like to read some more on membership on ASP.Net MVC3, since I already have a database in place with user credentials, I do not need the prapackaged one.
Thank you!
Its actually fairly easy to implement custom authentication code in ASP.NET MVC.
In the LogOn method of your controller you'll need to call the FormsAuthentication provider after you have authenticated the user's credentials.
public ActionResult LogOn(LogOnModel model)
{
//Handle custom authorization then call the FormsAuthentication provider
FormsAuthentication.SetAuthCookie(/*user name*/, true);
//Return view
}
After this method call, The User.Identity.Name will be populated, and you can utilize the AuthorizeAttribute on your controllers or controller methods.
In the LogOff method of your controller you'll call the FormsAuthentication provider again
public ActionResult LogOff()
{
FormsAuthentication.SignOut();
//Return view
}