Custom Authentication for different areas in mvc - asp.net-mvc

I have a MVC project with 2 areas: Admin and Client. I also have a login page in the main controller. What I want to do is to Authenticate a user based on its roles. If the user is for client they can't login to admin and the other way around.
For example if you try Localhost/admin, the code checks if the user is authorised. If not it redirects you to Localhost/admin/AccountLogin. The same for Localhost/client to Localhost/client/account/login.
I want to use a customAuthorize rather than [Authorize(Roles="Admin")].
everything works fine if I don't use roles, but the problem is if you login as client you can simply change the url and go to admin. So I tried to use roles.
In admin area:
An account Controller:
public class AccountController : MainProject.Controllers.AccountController
{ }
A home controller:
[CustomAuthorize("Admin")]
public class HomeController : Controller
{
public ActionResult HomePage()
{
return View();
}
}
The custom Authorise:
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
private string _loginPage { get; set; }
private string _customRole { get; set; }
public CustomAuthorizeAttribute(string userProfilesRequired)
{
_customRole = userProfilesRequired;
_loginPage = "/" + _customRole + "/Account/Login";
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
var formsIdentity = filterContext.HttpContext.User.Identity as System.Web.Security.FormsIdentity;
// I want to check if the role of current user is the same as the controller If not redirect to the /account/login page.
var validRole = this.Roles == _customRole;//filterContext.HttpContext.User.IsInRole(_customRole);
if (filterContext.HttpContext.User.Identity.IsAuthenticated)
{
if (!validRole)
{
filterContext.HttpContext.Response.Redirect(_loginPage);
}
}
else
{
filterContext.HttpContext.Response.Redirect(_loginPage);
}
base.OnAuthorization(filterContext);
}
}
The Account Controller in Main Controller:
public class AccountController : Controller
{
[AllowAnonymous]
public ActionResult Login()
{
return View();
}
//
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model, string ReturnUrl)
{
try
{
if (ModelState.IsValid)
{
if (model.UserName == "Arash" && model.Password == "123")
{
FormsAuthentication.SetAuthCookie(model.UserName, false);
//I need to set the roles here but not sure how
return RedirectToAction("homePage", "Home", new { area = GetArea() });
}
}
ModelState.AddModelError("", "The user name or password provided is incorrect.");
return View(model);
}
catch (Exception ex)
{
ModelState.AddModelError("", "Error: " + ex.Message);
return View(model);
}
}
}
and it the web config:
<forms loginUrl="~/Account/Login" timeout="200" />
</authentication>
<authorization>
<allow roles="Admin,Client" />
</authorization>
I searched a lot in the web but couldn't find a proper answer. I appreciate if you Could help me out to correctly implement this authorisation in MVC.
I just want to know how can I set a role to a user when login. At the moment if I set a user in login, it can't remember when it gets to CustomAuthorize class.
Any help?
Cheers,

There are a lot of ways to this but I will tell you what I used in this case.
You don't actually need to create a custom Authorization Attribute but instead make use of PostAuthenticateRequest event Handler in Global.asax given that you have a "table" roles in your database.
Add the code below in Global.asax
public override void Init()
{
this.PostAuthenticateRequest += new EventHandler(MvcApplication_PostAuthenticateRequest);
base.Init();
}
void MvcApplication_PostAuthenticateRequest(object sender, EventArgs e)
{
if (User.Identity.IsAuthenticated && User.Identity.AuthenticationType == "Forms")
{
string[] roles = GetRoleOfUser(Context.User.Identity.Name);
var newUser = new GenericPrincipal(Context.User.Identity, roles);
Context.User = Thread.CurrentPrincipal = newUser;
}
}
public string[] GetRoleOfUser(string username)
{
string[] usersInRole;
// Get the Role of User from the Database
// Should be of String Array
// Example Query to Database: 'Select UserRole FROM User WHERE Username = "arash"'
// It doesnt matter if user has One or more Role.
return usersInRole;
}
Then your account controller should be this.
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model, string ReturnUrl)
{
try
{
if (ModelState.IsValid)
{
if (model.UserName == "Arash" && model.Password == "123")
{
FormsAuthentication.SetAuthCookie(model.UserName, false);
HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
return RedirectToAction("HomePage", "Home");
}
}
ModelState.AddModelError("", "The user name or password provided is incorrect.");
return View(model);
}
catch (Exception ex)
{
ModelState.AddModelError("", "Error: " + ex.Message);
return View(model);
}
}
Now for example there is an Action in your HomeController that can only be access by Admin. You can just decorate the action with Authorize attribute like this below.
HomeController.cs
[Authorize(Roles = "Admin")]
public ActionResult AdminHomepage()
{
//For Admin Only
return View();
}
[Authorize(Roles = "Client")]
public ActionResult ClientHomepage()
{
//Client only Homepage, User with Role "Admin" cant go here.
return View();
}
[AllowAnonymous]
public ActionResult HomePageForAll()
{
//For Everyone
return View();
}
[Authorize(Roles = "Client,Admin")]
public ActionResult HomePageForClientAndAdmin()
{
return View();
}
public ActionResult HomePage()
{
return View();
}
The user will be redirected to Login URL if they are not authorized given that it is specified in Web.config (Which you already have set).

I have an action method and that can be accessed by Admin only
// Action Methods
[AuthorizationService] // My custom filter ,you can apply at controller level
public ActionResult ProjectList(Employee emp)
{
// do some work
}
//Employee class
public class Employee
{
string Name{get;set;}
string Role{get;set;}
}
// My custom filter
class AuthorizationService : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
Employee = filterContext.ActionParameters["emp"] as Employee;
if (Employee.Role!="Admin")
{
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary(
new { action = "Login", Controller ="Home"}));
}
}
}

Related

MVC HTTP Error 403.14 - Forbidden after create new record

1- AuthorizeUserAttribute.cs is class for costume authorize attribute
public class AuthorizeUserAttribute : AuthorizeAttribute
{
public string AccessLevel { get; set; }
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var isAuthorized = base.AuthorizeCore(httpContext);
if (!isAuthorized)
return false;
if (this.AccessLevel.Contains("Admin"))
{
return true;
}
else return false;
}
2- this is my controller
[AuthorizeUser(AccessLevel = "Admin")]
public class ProductsController : Controller
{
private DataBaseContext db = new DataBaseContext();
public ActionResult Index()
{
var product = db.Product.Include(p => p.ProductGroup);
return View(product.ToList());
}
}
[AuthorizeUser(AccessLevel = "Admin")]
public ActionResult Create([Bind(Include = "Product_Id,ProductName,Description,PicUrl,Group_Id")] Product product)
{
if (ModelState.IsValid)
{
db.Product.Add(product);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.Group_Id = new SelectList(db.ProductGroups, "Group_Id", "GreoupName", product.Group_Id);
return View(product);
}
3-FilterConfig.cs in start_up folder
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
filters.Add(new AuthorizeAttribute());
filters.Add(new AuthorizeUserAttribute());
}
}
4-Global.asax.cs
void Application_Start(object sender, EventArgs e)
{
// Code that runs on application startup
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
RouteConfig.RegisterRoutes(RouteTable.Routes);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier;
}
5- Admin1Controller.cs for login and etc...
[HttpPost]
public ActionResult Login(LoginViewModel model)
{
if (!ModelState.IsValid) //Checks if input fields have the correct format
{
return View(model); //Returns the view with the input values so that the user doesn't have to retype again
}
if(model.Email == "info#psmgroups.com" & model.Password == "#1234psm")
{
var identity = new ClaimsIdentity(new[] {
new Claim(ClaimTypes.Name,"Admin" ),
new Claim(ClaimTypes.Email, "info#psmgroups.com"),
new Claim(ClaimTypes.Role,"Admin")
}, "ApplicationCookie");
var ctx = Request.GetOwinContext();
var authManager = ctx.Authentication;
authManager.SignIn(identity);
return Redirect(GetRedirectUrl(model.ReturnUrl));
}
ModelState.AddModelError("", "incorrect UserName or pass");
return View(model);
}
after create new product and return to products/ show HTTP Error 403.14 - Forbidden page. while write product/Index show correct page
First, there's no code here that actually ever sets the AccessLevel property on your custom attribute. Maybe you just didn't post it, but if this is all your code, then it's fairly obvious why this doesn't work: AccessLevel is always null, and therefore never contains the string "Admin".
That said, you don't even need a custom attribute here. AuthorizeAttribute already handles roles. It seems you're trying to implement some sort of parallel role-like functionality, but that's a waste of time. Just do:
[Authorize(Roles = "Admin")]
And call it a day.

How to use Role Provider in MVC 5?

I am first time using Membership and Role provider for my login page.My membership was work fine but I couldn't able to use role provider on my login page.
I have one controller named MyAccount controller. This controller will verify user membership and after verified it will redirect to Home controller based on user Role.
Here is
MyAccount controller:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Login(Login l, string returnUrl = "")
{if (ModelState.IsValid)
{
var isValidUser = Membership.ValidateUser(l.UserName, l.Password);
if (isValidUser)
{
FormsAuthentication.SetAuthCookie(l.UserName, l.RememberMe);
if (Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
else If(*"USER ROLE AS ADMIN"*)
{
RedirectToAction("AdminIndex","Home");
}
else
{
RedirectToAction("ClientIndex","Home");
}
}
}
ViewBag.ErrorMassage = "Wrong Id or Password";
ModelState.Remove("Password");
return View();
}
And
Home Controller:
[Authorize (Roles= "Admin")]
public ActionResult AdminIndex()
{
return View();
}
[Authorize (Roles = "Client")]
public ActionResult ClientIndex()
{
return View();
}
I am not sure Where I should check for User Role, In MyAccount Controller or In Home Controller?
RoleProvider:
public override string[] GetRolesForUser(string username)
{
if (!HttpContext.Current.User.Identity.IsAuthenticated)
{
return null;
}
//check cache
var cacheKey = string.Format("{0}_role", username);
if (HttpRuntime.Cache[cacheKey] != null)
{
return (string[])HttpRuntime.Cache[cacheKey];
}
string[] roles = new string[] { };
roles = gateway.GetUserRole(username);
{
if (roles.Any())
{
HttpRuntime.Cache.Insert(cacheKey, roles, null, DateTime.Now.AddMinutes(_cacheTimeoutInMinute), Cache.NoSlidingExpiration);
}
}
return roles;
}
public override bool IsUserInRole(string username, string roleName)
{
var userRoles = GetRolesForUser(username);
return userRoles.Contains(roleName);
}
How can I Use this RoleProvider to my controller and redirect it to Admin or Client Action?
Register your custom role provider in web.config:
<roleManager defaultProvider="DefaultRoleProvider">
<providers>
<add name="DefaultRoleProvider" type="MyNamespace.MyRoleProvider, MyAssembly" />
</providers>
</roleManager>
Update Login action method on the MyAccount controller:
if (Roles.IsUserInRole("Admin"))

Inconsistent accessibility: parameter type is less accessible

public class UserController : Controller
{
//
// GET: /User/
public ActionResult Register()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Register(User U)
{
if (ModelState.IsValid)
{
using (MyDatabaseEntities dc = new MyDatabaseEntities())
{
dc.Users.Add(U);
dc.SaveChanges();
ModelState.Clear();
U = null;
ViewBag.Message = "Successfully register Done";
}
}
return View(U);
}
}
I suspect, but without the full error message giving us the type and location in the code it is something of a guess, that type User is protected or internal.

MVC 4 Redirect User After LogIn

How can I redirect user after successful login to specific page. I need to do this by Authorize Attribute. In my problem I have to redirect user to page: Index/About when he/she would use specific login name, rest of users would be redirected to Index/Home.
I try this method:
public class AuthAttribute : AuthorizeAttribute
{
private string userName;
protected override bool AuthorizeCore(HttpContextBase filterContext)
{
var authorized = base.AuthorizeCore(filterContext);
if (!authorized)
{
return false;
}
string userAuthenticated = filterContext.User.Identity.Name;
bool specialUser = userName.Equals(userAuthenticated, StringComparison.OrdinalIgnoreCase);
if (specialUser)
{
filterContext.Items["RedirectToHomeAbout"] = true;
return false;
}
return true;
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
if (filterContext.HttpContext.Items.Contains("RedirectToHomeAbout"))
{
var routeValues = new RouteValueDictionary(new
{
controller = "Home",
action = "About",
});
filterContext.Result = new RedirectToRouteResult(routeValues);
}
else
{
base.OnAuthorization(filterContext);
}
}
This is the code from account controller:
[Authorize]
[InitializeSimpleMembership]
public class AccountController : Controller
{
//
// GET: /Account/Login
[AllowAnonymous]
public ActionResult Login(string returnUrl)
{
ViewBag.ReturnUrl = returnUrl;
return View();
}
//
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model, string returnUrl)
{
if (ModelState.IsValid && WebSecurity.Login(model.UserName, model.Password, persistCookie: model.RememberMe))
{
return RedirectToLocal(returnUrl);
}
// If we got this far, something failed, redisplay form
ModelState.AddModelError("", "The user name or password provided is incorrect.");
return View(model);
}
//
// POST: /Account/LogOff
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LogOff()
{
WebSecurity.Logout();
return RedirectToAction("Index", "Home");
}
Thanks!
MarQ
Instead of override OnAuthorization, you might want to override HandleUnauthorizedRequest.
In this way, the code would look like:
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) {
if (filterContext.HttpContext.Items.Contains("RedirectToHomeAbout"))
{
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary
{
{ "action", "About" },
{ "controller", "Home" }
});
}
else
{
//your general redirect here
}
}
If I understand your requirement correctly you can do it in your AccountController:
private string specificUserName = "specificUserName";
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model, string returnUrl)
{
if (ModelState.IsValid && WebSecurity.Login(model.UserName, model.Password, persistCookie: model.RememberMe))
{
if (string.Equals(model.UserName, specificUserName))
{
return RedirectToAction("Index", "About");
}
return RedirectToAction("Index", "Home");
}
// If we got this far, something failed, redisplay form
ModelState.AddModelError("", "The user name or password provided is incorrect.");
return View(model);
}

Redirect a user to a specific view when authorization fails?

I have the following code:
[AcceptVerbs(HttpVerbs.Post), Authorize(Roles = RoleKeys.Administrators)]
public ActionResult Edit(int id, FormCollection collection)
{
User user = userRepository.GetUser(id);
try
{
this.UpdateModel(user);
userRepository.Save();
return this.RedirectToAction("Details", new { id = user.UserId });
}
catch
{
this.ModelState.AddModelErrors(user.GetRuleViolations());
return View(new UserFormViewModel(user));
}
}
If the currently logged in user is not in the Administrators role, it kicks them back to the login screen. The user is already logged in, they are just not authorize to perform the requested action.
Is there any way to have them redirected to a specific view, for example, AccessDenied?
You can define your own attribute:
public class MyAuthorizeAttribute: AuthorizeAttribute
{
public override void OnAuthorization( AuthorizationContext filterContext )
{
base.OnAuthorization(filterContext);
if (filterContext.Result is HttpUnauthorizedResult)
{
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary
{
{ "controller", "Login" },
{ "action", "AccessDenied" }
});
}
}
}
and use
[AcceptVerbs(HttpVerbs.Post), MyAuthorize(Roles = RoleKeys.Administrators)]

Resources