I recently added Forms-based Authentication to an MVC 3 project. There seems to be a problem between my Application_AuthenticateRequest function(Global.asax.cs file) and my settings for my Web.Config file because my Application_AuthenticateRequest function seems to get called infinitely. How can I change my configurations for this to work properly and how can I allow a user access to both the login page and the default page, while still denying access to the other pages?
//Global.asax.cs
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
// Extract the forms authentication cookie
string cookieName = FormsAuthentication.FormsCookieName;
HttpCookie authCookie = Context.Request.Cookies[cookieName];
if (null == authCookie)
{
// There is no authentication cookie.
return;
}
FormsAuthenticationTicket authTicket = null;
try
{
authTicket = FormsAuthentication.Decrypt(authCookie.Value);
}
catch (Exception ex)
{
// Log exception details (omitted for simplicity)
return;
}
if (null == authTicket)
{
// Cookie failed to decrypt.
return;
}
string[] roles = authTicket.UserData.Split('|');
// Create an Identity object
FormsIdentity id = new FormsIdentity(authTicket);
// This principal will flow throughout the request.
UserPrincipal principal = new UserPrincipal(id, roles);
// Attach the new principal object to the current HttpContext object
Context.User = principal;
Thread.CurrentPrincipal = principal;
}
//Web.Config
<authentication mode="Forms">
<forms loginUrl="~/Account/LogOn"
protection="All"
cookieless="UseCookies"
slidingExpiration="false"
timeout="30" />
</authentication>
<authorization>
<deny users="?" />
<allow users="*"/>
</authorization>
Per the comments on Rob's answer...
"So technically, I need all pages blocked except for default, login and registration pages."
You can add the AuthorizeAttribute to the GlobalFilterCollection which is a collection of filters that get applied to all actions on controllers. Then, on your controllers or actions you can add [AllowAnonymous] to the specific ones you want anyone to access. See below for an example.
Create a file called FIlterConfig.cs in the App_Start folder
If this exists already, just add the line: filters.Add(new AuthorizeAttribute());
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
filters.Add(new AuthorizeAttribute());
}
}
This will require every Controller and Action to use Authorization by default.
You can make a Controller or Action not authorized by adding the following to your Action or Controller like so.
[AllowAnonymous]
public class MyController
{
public ActionResult MyAction()
{
return View();
}
}
All actions in that controller will be available.
OR
public class MyController
{
[AllowAnonymous]
public ActionResult MyAction()
{
return View();
}
}
Only that action on the controller will be available.
This will be called for every request, not just when the user logs in for the first time.
You can use the [Authorize] attribute to limit access to certain controllers or even methods.
I'd suggest looking through some tutorials or the documents to understand how authentication works in MVC:
http://msdn.microsoft.com/en-us/library/ff398049(v=vs.98).aspx
Related
I am trying to implement a global exception handling technique to avoid using try/catch blocks on every action of my controllers but my application crashes before the overridden OnException method or the ErrorHandleAttribute are reached.
Here is a simplified testing code:
[HandleError]
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult Test()
{
throw new Exception("Exception Test");
}
protected override void OnException(ExceptionContext filterContext)
{
if (filterContext == null || filterContext.ExceptionHandled)
{
return;
}
var message = filterContext.Exception.Message;
filterContext.Result = new HttpStatusCodeResult(HttpStatusCode.BadRequest, message);
filterContext.ExceptionHandled = true;
}
}
and in Web.Config:
<system.web>
<customErrors mode="On" defaultRedirect="Error"/>
If I set a break point on the 'throw' line and try to access /Home/Test my application will crash and both OnException method and the HandleErrorAttribute will be reached only after that.
Am I missing something?
Thank you.
You need to register the HandleError filter.
filters.Add(new HandleErrorAttribute{View = "Error"});
I am developing a cloud application on ASP.Net MVC. I have problems in implementation of login system.
What I did
I used FormsAuthentication.SetAuthCookie(...) for login of web users.
and Authorize in controller like that
[Authorize]
[HttpGet]
public ActionResult AdminPage(){...}
What the Problem is
When i sign in with web users credentials and tries to access admin controllers it have nothing to stop that user and he can access all(including admin pages).
What type of Login implimentation should i use to tackle this. Remember keep the security in mind.
I also saw ASP.Net MemberShip Class but it generates Database on its own. Can it be mold and works good with user define Database.
You can specify roles on the Authorize attribute.
Example
[Authorize(Roles="Admin")]
This means that only users that have a role of admin will have access to that method.
Update
If you are using custom roles, you will need to create a Custom Role Provider. This will allow you to override the RoleProvider methods and add your own logic.
Example
public class CustomRoleProvider : RoleProvider
{
public override bool IsUserInRole(string username, string roleName)
{
// Custom logic
}
public override string[] GetRolesForUser(string username)
{
// Custom logic
}
public override string[] GetAllRoles()
{
// Custom logic
}
}
A detailed tutorial can be found here Custom Role Providers
I do not want to steal Colin Bacon's answer. Instead, I'll add some additional information; I hope second opinion won't hurt.
You need [Authorize(Roles = "Admin")] to restrict access to Admin role only like this -
[Authorize(Roles = "Admin")]
public ActionResult AdminPage(){...}
Since your MVC application already uses FormsAuthentication, you do not need to use MembershipProvider.
However, you need to implement Custom Role Provider and override GetRolesForUser method (the rest of the methods are optional).
Basically, AuthorizeAttribute will call GetRolesForUser method when a user access an action with [Authorize(Roles = "Admin")].
public class CustomRoleProvider : RoleProvider
{
public override string[] GetRolesForUser(string username)
{
// Query admin table.
if(user is in admin table)
return new[] {"Admin"};
return new[] {};
}
....
}
first in web.config add this (system.web)
<roleManager enabled="true" defaultProvider="simple">
<providers>
<clear />
<add name="simple" type="WebMatrix.WebData.SimpleRoleProvider,WebMatrix.WebData" />
</providers>
</roleManager>
<membership defaultProvider="simple">
<providers>
<clear />
<add name="simple" type="WebMatrix.WebData.SimpleMembershipProvider,WebMatrix.WebData" />
</providers>
</membership>
then add this line to Global.asax in Application_Start
WebMatrix.WebData.WebSecurity.InitializeDatabaseConnection("DefaultConnection",
"UserProfile", "UserId", "UserName", autoCreateTables: true);
then you can add users to roles like this
var roles = (SimpleRoleProvider)Roles.Provider;
roles.AddUsersToRoles(new[] { "username1", "username2", ... },
new[] { "role1", "role2", .... });
then on the controllers or methods you can add AuthorizeAttribute like below
[Authorize(Roles="role1,role2,...")]
I have code written for MVC 4 using visual studio 2012... and i am trying to achieve role based authorization but it seem [Authorize] is not working for some reason.... and i still can get page through url even its controller action is initialize with [Authorize] attribute... second can i even try to look whole application by putting following code in global
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new System.Web.Mvc.AuthorizeAttribute());
filters.Add(new HandleErrorAttribute());
}
my routing default set to login page as no unauthorized person can access to app
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Account", action = "Login", id = UrlParameter.Optional }
);
}
and i trying to test by getting aa() view from directly url
[Authorize]
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
[Authorize]
public ActionResult aa()
{
return View();
}
Login code
[Authorize]
[InitializeSimpleMembership]
public class AccountController : Controller
{
public ActionResult Login(string returnUrl)
{
ViewBag.ReturnUrl = returnUrl;
return View();
}
[HttpPost]
public ActionResult Login(LoginModel model, string returnUrl)
{
if (ModelState.IsValid && WebSecurity.Login(model._UserName, model._Password, persistCookie: false))
{
return RedirectToAction("Index", "Home");
}
ModelState.AddModelError("", "The user name or password provided is incorrect.");
return View(model);
}
and web.config
<authentication mode="Forms">
<forms loginUrl="~/Account/Login" timeout="2880" />
</authentication>
InitializeSimpleMembership needs to decorate your HomeController, NOT the AccountsController. As your HomeController/Index method is the entry point of your whole website, for [Authorize] to work, you need to have SimpleMembership initialized first, before anything else.
I've created new MVC4 website and faced same problem: how to force authorization (and redirect to login page if not authorized).
I'm beginner in ASP.NET, and couldn't find simple answer (there is a lot about this, especially at stackoverflow but no one of these answers helped me). I've finally get it done after hours, so maybe I will help someone with this.
Add filter to global filters, which will require authorization everywhere (unless you'll set AllowAnonymous, but about it later). Open App_Start/FilterConfig.cs and:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
// Add this line
filters.Add(new AuthorizeAttribute());
}
Now we need to set authorization in config file. Open Web.config in root dir of project and:
<system.web>
// start of modification
// remove this line
<authentication mode="None" />
// add this lines
<authentication mode="Forms">
<forms loginUrl="~/Account/Login" timeout="30" protection="All"></forms>
</authentication>
<authorization>
<deny users="?" />
</authorization>
// end of modification
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5" />
</system.web>
And thats all. Default login actions have [AllowAnonymous] tag by default so it just should work fine. It they not you need add them manually in Controllers/ like that:
[AllowAnonymous]
public ActionResult Login(string returnUrl)
{
ViewBag.ReturnUrl = returnUrl;
return View();
}
Hope helped someone.
In my ASP.NET MVC application, I'm trying to figure out whether the user has access to a particular controller, restricted by the authorize data annotation as follows
[Authorize(Roles = "user")]
I'm attempting to override OnAuthorization in order to check:-
If the request is authenticated (which works great)
If the user is authorised to access the requested view (which doesn't work)
My user roles are stored in a SessionManager object I've created - SessionManager.ActiveUser.Roles
Here's what I have in the form of pseudo-code but if anybody could help me get this right, I'd really appreciate it.
public class HomeBaseController : Controller
{
protected override void OnAuthorization(AuthorizationContext context)
{
if (context.HttpContext.User.Identity.IsAuthenticated)
{
// these values combined are our roleName
bool isAuthorised = context.HttpContext.User.IsInRole(context.RequestContext.HttpContext.User.Identity.);
if (!context.HttpContext.User.IsInRole(---the roles associated with the requested controller action (e.g. user)---))
{
var url = new UrlHelper(context.RequestContext);
var logonUrl = url.Action("LogOn", "SSO", new { reason = "youAreAuthorisedButNotAllowedToViewThisPage" });
context.Result = new RedirectResult(logonUrl);
return;
}
}
}
As far as overriding OnAuthorization according to ProASP.NET MVC3 Book they do not recommend overriding it since the default implementation of this method securely handles content cached using OutputCache Filter.
If you are looking for Custom Authentication (using Forms Auth) and Authorization (Using Role provider logic then below is how I secured my application.
EDIT: The following logic uses in-built forms authentication and roles manager. Once user is authenticated and authorized the User Identity can be used to check both the authentication (User.Identity.IsAuthenticated) and the roles User.IsInRole("admin")
In Web.Config:
<authentication mode="Forms">
<forms loginUrl="~/Account/LogOn" timeout="15" slidingExpiration="true" enableCrossAppRedirects="false" protection="All" />
</authentication>
<roleManager enabled="true" defaultProvider="MyRolesProvider" cacheRolesInCookie="true" cookieProtection="All">
<providers>
<clear />
<add name="MyRolesProvider" type="MyApp.Library.CustomRolesProvider" />
</providers>
</roleManager>
For Role Authorization Extend RoleProvider and override methods as required.
public class CustomRolesProvider : RoleProvider
{
public override string[] GetRolesForUser(string username)
{
// You need to return string of Roles Here which should match your role names which you plan to use.
//Some Logic to fetch roles after checking if User is Authenticated...
return new string[] { "admin" , "editor" };
}
//Rest all of them I have kept not implemented as I did not need them...
}
In Your controller Now you can use this:
[Authorize(Roles="Admin")]
public class AdminController : Controller
{
....
}
For Authentication I have implemented my custom Authentication Check but I still use Forms Authentication:
//This one calls by Custom Authentication to validate username/password
public ActionResult LogOn(LogOnViewModel model, string returnUrl)
{
if(Authenticate("test","test"))
{
.......
}
}
public bool Authenticate(string username, string password)
{
//Authentication Logic and Set the cookie if correct else false.
//..... your logic....
//.....
FormsAuthentication.SetAuthCookie(username, false);
}
So I have an asp.net Web Application (Not Web Site) that I am trying to support profiles for anonymous users. I have a form and I want anonymous users to be able to enter their name and email only once, and have that information automatically accessible on the next load for them.
In my Web.config I have anonymous ID setup like so:
<anonymousIdentification enabled="true" cookieless="AutoDetect" />
I have my profile section setup like this:
<profile defaultProvider="SqlProvider" enabled="true" inherits="QA_Web_Tools.UserProfile">
<providers>
<clear />
<add connectionStringName="QAToolsConnectionString" name="SqlProvider"
type="System.Web.Profile.SqlProfileProvider" />
</providers>
</profile>
Finally, due to my app being a Web App and not a Web Site, I am using the profiles via this custom object:
public class UserProfile : ProfileBase
{
public static UserProfile GetUserProfile(string username)
{
return Create(username) as UserProfile;
}
public static UserProfile GetUserProfile()
{
return Create(Membership.GetUser().UserName) as UserProfile;
}
[SettingsAllowAnonymous(true)]
public string FullName
{
get { return base["FullName"] as string; }
set { base["FullName"] = value; }
}
[SettingsAllowAnonymous(true)]
public string BuildEmail
{
get { return base["BuildEmail"] as string; }
set { base["BuildEvmail"] = value; }
}
}
This code is based off of this reference.
The issue is that that code does not support anonymous users, or if it does I don't know how. I can't use the GetUserProfile() method with no parameters because if the user is anonymous, Membership.GetUser() is null. I could pass in the anonymous ID token into the first GetUserProfile(string username) method but I cant' find any way to get the anonymous ID token for the current user.
Does anyone know how to get this information? Google doesn't seem to be returning useful results.
Thanks,
Success!
I changed:
public static UserProfile GetUserProfile()
{
return Create(Membership.GetUser().UserName) as UserProfile;
}
to
public static UserProfile GetUserProfile()
{
return HttpContext.Current.Profile as UserProfile;
}
and now it works!