Authorization roles not working - c#

Here's my Sign Up and Login action methods in the controller:
public ActionResult SignUp()
{
return View();
}
[HttpPost]
public ActionResult SignUp(User _user)
{
_user.Authorize = CustomRoles.RegisteredUser;
int lastuserid = entities.Users.Last().UserID;
_user.UserID = lastuserid + 1;
if (ModelState.IsValid)
{
Roles.AddUserToRole(_user.UserName, CustomRoles.RegisteredUser);
entities.Users.Add(_user);
entities.SaveChanges();
RedirectToAction("Index");
}
return View(_user);
}
public ActionResult Login()
{
LoginViewModel LVM = new LoginViewModel();
HttpCookie existingCookie = Request.Cookies["UserName"];
if (existingCookie != null)
{
LVM.UserName = existingCookie.Value;
}
return View(LVM);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginViewModel u)
{
if (ModelState.IsValid)
{
if (u.RememberMe==true)
{
HttpCookie existingCookie = Request.Cookies["UserName"];
if (existingCookie != null)
{
existingCookie.Value = u.UserName;
existingCookie.Expires = DateTime.Now.AddHours(-20);
}
HttpCookie newCookie = new HttpCookie("UserName", u.UserName);
newCookie.Expires = DateTime.Today.AddMonths(12);
Response.Cookies.Add(newCookie);
}
var v = entities.Users.Where(a => a.UserName.Equals(u.UserName) && a.Password.Equals(u.Password)).FirstOrDefault();
if (v != null)
{
System.Web.HttpContext.Current.Session["UserName"] = u.UserName;
return RedirectToAction("Index");
}
}
return View(u);
}
and here's a sample of the action methods they're supposed to go to, some of them are in a different controller but the outcome is the same for all of them:
[Authorize(Roles = CustomRoles.RegisteredUser)]
public ActionResult Orders(User U)
{
return View();
}
[Authorize(Roles = CustomRoles.Manager)]
public ActionResult Stock()
{
return View(entities.Cars.ToList());
}
what happens is i get redirected back to the Login method, which is what should be happening if a user isn't logged in, but a user is logged in and it's still hapening

You are trying to implement Form Authorization, but as i think you forgot about authorize attribute use HttpContext.User.IsInRole method to detect is user can access to action. To resolve your problem you can configure forms auth via your web.config or you manually assign your HttpContext.User via HttpModule or with application events in Global.asax.cs like this for example:
protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
{
HttpContext context = ((HttpApplication)sender).Context;
HttpCookie existingCookie = Request.Cookies["UserName"];
if (existingCookie != null) {
context = new new GenericPrincipal(new GenericIdentity(existingCookie.Value), new string[]{"Admin", "Manager"});
}
}

Related

Problem when disconnecting a user in ASP.NET MVC

I have a problem with ASP.Net MVC regarding authentication. The user managed to login and log out with no problem but when I click the back button is in the browser on the watch still logged in !!!
Can someone help me!!!
I also remind you that I am not using the default authentication of Visual Studio
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginViewModel model)
{
if (ModelState.IsValid)
{
var isValidUser = IsValidUser(model);
if(isValidUser != null)
{
FormsAuthentication.SetAuthCookie(model.UserMail, true);
return RedirectToAction("Index", "Home");
}
else
{
ModelState.AddModelError("Eror", "Invalid login attempt");
return View();
}
}
else
{
return View(model);
}
}
public User IsValidUser(LoginViewModel model)
{
using(var db = new DbCaimanContext())
{
User user = db.Users.Where(q => q.UserMail.Equals(model.UserMail) && q.Password.Equals(model.Password)).SingleOrDefault();
if (user == null)
return null;
else
return user;
}
}
And here is my disconnection method :
public ActionResult LogOut()
{
FormsAuthentication.SignOut();
Session.Abandon();
return RedirectToAction("Login");
}
In your Login Get Method
[AllowAnonymous]
public ActionResult Login(string returnUrl)
{
ViewBag.ReturnUrl = returnUrl;
if (HttpContext.User.Identity.IsAuthenticated)
return RedirectToAction("Index", "Main");// go to anywhere you want
else
return View();
}

Mvc, Authorize bounces authorized users

I'm trying to make a section of a MVC 5 webpage restricted to users of a certain Active directory group, however the [Authorize] attribute (on controller) blocks logged in users aswell.
My Login page code behind looks as follows:
public class AccountController: Controller
{
[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)
{
ActiveDirectoryHelper ad = new ActiveDirectoryHelper();
if (Membership.ValidateUser(model.UserName, model.Password))
{
if (ad.CheckGroupMembership(model.UserName))
{
FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
if (Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/")
&& !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\\"))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
else
{
ModelState.AddModelError("", "Credentials are correct but you are no authorised \n You Need membership in group: HKF-HIT-FortigateAPI-GS");
}
}
else
{
ModelState.AddModelError("", "The user name or password provided is incorrect");
}
}
// if we got this far, something failed, redisplay form
return View(model);
}
// POST: /Account/LogOff
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LogOff()
{
FormsAuthentication.SignOut();
return RedirectToAction("Index", "Home");
}
}
public class ActiveDirectoryHelper
{
string group = "HKF-HIT-FortigateAPI-GS";
public bool CheckGroupMembership(string name)
{
var context = new PrincipalContext(
ContextType.Domain,
"AD-Domain", #"Username", "Password");
var userPrincipal = UserPrincipal.FindByIdentity(
context,
IdentityType.SamAccountName,
name);
var test = userPrincipal;
if (userPrincipal.IsMemberOf(context,
IdentityType.Name,
group))
{
return true;
}
return false;
}
}
The user passes and is redirected to Index in Home controller.
This controller however has the [Authorized] value set as follows:
[Authorize]
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
}
And here the user in bounced back to the loginpage, as if he was not Authorized.
Also this is web.config:
In the browser i can see the ADAuthCookie.
Edit: Ading pictures of Request data:
Account Post:
Fiddler:
Index Get:
Fiddler:
EDIT: Question has been solved, after going trough the amazing guide linked by in the comments i realised i was never handling my cooke in the Global.asaz.cs Class.
Adding an overide to Application_PostAuthenticateRequest solved my problem.
The code i added ended up using:
protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
{
HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
JavaScriptSerializer serializer = new JavaScriptSerializer();
CustomPrincipalSerializeModel serializeModel = serializer.Deserialize<CustomPrincipalSerializeModel>(authTicket.UserData);
CustomPrincipal newUser = new CustomPrincipal(authTicket.Name);
newUser.Name = serializeModel.Name;
HttpContext.Current.User = newUser;
}
}
In global.asax and i also added:
CustomPrincipalSerializeModel serializeModel = new CustomPrincipalSerializeModel();
serializeModel.Name = model.UserName;
JavaScriptSerializer serializer = new JavaScriptSerializer();
string userData = serializer.Serialize(serializeModel);
FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(
1,
model.UserName,
DateTime.Now,
DateTime.Now.AddMinutes(15),
false,
userData);
string encTicket = FormsAuthentication.Encrypt(authTicket);
HttpCookie faCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);
Response.Cookies.Add(faCookie);
To my login page.
AuthorizeAttribute checks the HttpContext.User value (an IPrincipal implementation) and the HttpContext.User.Identity value (an IIdentity implementation).
All of the security frameworks (Identity, Membership, etc.) from Microsoft use these interfaces to communicate with MVC/ASP.NET. If you are using a custom security framework, you also need to implement these interfaces and set them in the AcquireRequestState (if using session state) or PostAuthorizeRequest event.
See ASP.NET MVC - Set custom IIdentity or IPrincipal for an example of the latter along with custom IPrincipal and IIdentity implementations.

Why cookie request is null return in mvc controller?

My login methot,
A cookie incoming data is put,
public async Task<ActionResult> Login(string username, string password)
{
try
{
RepairService.EmployeeServiceClient srv = new RepairService.EmployeeServiceClient();
var CurrentEmployee = await Task.Run(() => srv.LoginEmployee(username, password));
if (CurrentEmployee != null)
{
var model = new EmployeeDetail
{
EmloyeeId = CurrentEmployee.EmployeeId,
//...//
};
HttpCookie cookie = new HttpCookie("userCookie");
cookie.Value = JsonConvert.SerializeObject(model);
cookie.Expires = DateTime.Now.AddDays(1);
Response.Cookies.Add(cookie);
return Json(model);
}
}
catch (Exception)
{
return Json(null);
}
return Json(false);
}
So you can control when users login cookie is not empty.
public ActionResult IsLoggedIn()
{
return Json(Request.Cookies["userCookie"] != null && Request.Cookies["userCookie"].Value != "");
}
When I call to get the value of this method Request is null. Where can I make mistakes ?
public ActionResult GetCurrentUser()
{
if (Request.Cookies["userCookie"] != null && Request.Cookies["userCookie"].Value == "Test")
{
var employee = (EmployeeDetail)JsonConvert.DeserializeObject(Request.Cookies["userCookie"].Value);
return Json(employee);
}
return Json(false);
}
In my opinion you're doing your Auth verification in the wrong way. You should not create an Action in your Controller responsible for that. You must create an ActionFilter, and decorate your Actions or your Controller's, or even register this filter globally to verify this for every request. For example:
ActionFilter
public class AuthorizedFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
//Do your logic here
var cookieValue = filterContext.HttpContext.Request.Cookies["userCookie"]).FirstOrDefault();
if(cookieValue != null)
return;
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary
{
{"controller", "Home"},
{"action", "Login"},
});
}
}
Controller
You could use at Controller Level, wich means, it will apply to all Actions
[AuthorizedFilter]
public class SomeController : Controller
{
public ActionResult Index()
{
return View();
}
}
Or you could use at Action level
public class SomeController : Controller
{
[AuthorizedFilter]
public ActionResult Index()
{
return View();
}
}

Role-Based Authentication with aspnet identity in ASP.NET MVC 4

I'm creating ASP.NET MVC 4 Internet Application.
In that Application I created Login Page that any user can log in to, then I allowed to redirect user to different pages based on their role.
ASP.NET Identity is the membership system here.
This is my Login Controller method:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (ModelState.IsValid)
{
var user = await UserManager.FindAsync(model.UserName, model.Password);
if (user != null)
{
if (user.ConfirmedEmail == true)
{
await SignInAsync(user, model.RememberMe);
if (String.IsNullOrEmpty(returnUrl))
{
if (UserManager.IsInRole(user.Id, "HEC_Admin"))
{
return RedirectToAction("Index", "HEC");
}
//role Admin go to Admin page
if (UserManager.IsInRole(user.Id, "HEI_User"))
{
return RedirectToAction("Index", "HEI");
}
}
else
{
return RedirectToLocal(returnUrl);
}
}
else
{
ModelState.AddModelError("", "Confirm Email Address.");
}
}
else
{
ModelState.AddModelError("", "Invalid username or password.");
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
This is HEI Controller Class:
public class HEIController : Controller
{
//
// GET: /HEI/
[Authorize(Roles = "HEI_User")]
public ActionResult Index()
{
return View();
}
}
This is my HEC Controller Class:
public class HECController : Controller
{
//
// GET: /HEC/
[Authorize(Roles = "HEC_Admin")]
public ActionResult Index()
{
return View();
}
}
when I remove [Authorize(Roles = "HEC_Admin")] above the index action in HECController class and when I remove [Authorize(Roles = "HEC_User")] above the index action in HEIController class this is working fine,
but then How restrict unauthorized access to these pages?
I had the same problem as you and I still don't know the reason why it happens. What I did was to create my own custom Authorization Attribute and check the Roles myself.
public class CustomAuthorizationAttribute : AuthorizeAttribute
{
public string IdentityRoles
{
get { return _identityRoles ?? String.Empty; }
set
{
_identityRoles = value;
_identityRolesSplit = SplitString(value);
}
}
private string _identityRoles;
private string[] _identityRolesSplit = new string[0];
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
//do the base class AuthorizeCore first
var isAuthorized = base.AuthorizeCore(httpContext);
if (!isAuthorized)
{
return false;
}
if (_identityRolesSplit.Length > 0)
{
//get the UserManager
using(var um = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext())))
{
var id = HttpContext.Current.User.Identity.GetUserId();
//get the Roles for this user
var roles = um.GetRoles(id);
//if the at least one of the Roles of the User is in the IdentityRoles list return true
if (_identityRolesSplit.Any(roles.Contains))
{
return true;
}
}
return false;
}
else
{
return true;
}
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
//if the user is not logged in use the deafult HandleUnauthorizedRequest and redirect to the login page
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
base.HandleUnauthorizedRequest(filterContext);
}
else
//if the user is logged in but is trying to access a page he/she doesn't have the right for show the access denied page
{
filterContext.Result = new RedirectResult("/AccessDenied");
}
}
protected static string[] SplitString(string original)
{
if (String.IsNullOrEmpty(original))
{
return new string[0];
}
var split = from piece in original.Split(',')
let trimmed = piece.Trim()
where !String.IsNullOrEmpty(trimmed)
select trimmed;
return split.ToArray();
}
}
I also added the HandleUnauthorizedRequest method to redirect to a appropriated page if the user has is logged in but has no access to this action or controller
To use it just do this:
[CustomAuthorization(IdentityRoles = "HEI_User")]
public ActionResult Index()
{
return View();
}
Hope it helps.

How to avoid ViewBag (or ViewData) in favor of a Model?

This is a very simple example, but it should be enough to demonstrate my issue. I need to pass a model to my view that the user will update, but the view also needs some other data to create a dropdownlist or to provide other information.
Based on my code below, I want to avoid use of ViewBag/ViewData, so do I somehow combine QuestionList and PasswordLength (thrown in for a superfluous example scenario) into the ChangeSecurityQuestionModel or create a new ViewModel or some other object?
[Authorize]
public ActionResult ChangeSecurityQuestion() {
var user = Membership.GetUser();
if (user != null) {
var model = new ChangeSecurityQuestionModel() {
PasswordQuestion = user.PasswordQuestion
};
ViewBag.QuestionList = new SelectList(membershipRepository.GetSecurityQuestionList(), "Question", "Question");
ViewBag.PasswordLength = MembershipService.MinPasswordLength;
return View(model);
}
// user not found
return RedirectToAction("Index");
}
[Authorize]
[HttpPost]
public ActionResult ChangeSecurityQuestion(ChangeSecurityQuestionModel model) {
if (ModelState.IsValid) {
var user = Membership.GetUser();
if (user != null) {
if (user.ChangePasswordQuestionAndAnswer(model.Password, model.PasswordQuestion, model.PasswordAnswer)) {
return View("ChangeQuestionSuccess");
} else {
ModelState.AddModelError("", "The password is incorrect.");
}
}
}
// If we got this far, something failed, redisplay form
ViewBag.QuestionList = new SelectList(membershipRepository.GetSecurityQuestionList(), "Question", "Question");
ViewBag.PasswordLength = MembershipService.MinPasswordLength;
return View(model);
}
Why not put QuestionList and PasswordLength in your ChangeSecurityQuestionModel
var model = new ChangeSecurityQuestionModel() {
PasswordQuestion = user.PasswordQuestion,
QuestionList = new SelectList(membershipRepository.GetSecurityQuestionList(), "Question", "Question"),
PasswordLength = MembershipService.MinPasswordLength;
};
One alternative to the recurring "do-I-use ViewState or keep adding to a Model" problem is to create extension methods for HtmlHelper:
public static class HtmlExtensions
{
public static MvcHtmlString SecurityQuestionDropDown(this HtmlHelper helper)
{
return helper.DropDownList(....,new SelectList(membershipRepository.GetSecurityQuestionList(), "Question", "Question"));
}
}
You could add the QuestionList and PasswordLength properties to your ChangeSecurityQuestionModel view model. And then:
[Authorize]
public ActionResult ChangeSecurityQuestion() {
var user = Membership.GetUser();
if (user != null) {
var model = new ChangeSecurityQuestionModel() {
PasswordQuestion = user.PasswordQuestion,
QuestionList = new SelectList(membershipRepository.GetSecurityQuestionList(), "Question", "Question"),
PasswordLength = MembershipService.MinPasswordLength
};
return View(model);
}
// user not found
return RedirectToAction("Index");
}
[Authorize]
[HttpPost]
public ActionResult ChangeSecurityQuestion(ChangeSecurityQuestionModel model) {
if (ModelState.IsValid) {
var user = Membership.GetUser();
if (user != null) {
if (user.ChangePasswordQuestionAndAnswer(model.Password, model.PasswordQuestion, model.PasswordAnswer)) {
return View("ChangeQuestionSuccess");
} else {
ModelState.AddModelError("", "The password is incorrect.");
}
}
}
// If we got this far, something failed, redisplay form
model.QuestionList = new SelectList(membershipRepository.GetSecurityQuestionList(), "Question", "Question");
model.PasswordLength = MembershipService.MinPasswordLength;
return View(model);
}

Categories