ASP.NET MVC Session is null - c#

I have been assigned to customize this MVC application. It has Forms Authentication, and I was instructed to customize it so that the "Login" Process is eliminated and all users can access the app. The application was built so that all operations are done around the logged in User.
Since I needed an authenticated user, I took the code which was responsible to log in and authenticate the user and put it in Application_AcquireRequestState
This is my Login Code:
public Action Login(string username, string password)
{
bool loginResult = false;
try
{
// If validated, it sets the LoginStatus to true and populates the Variables for security
bool validate = this.memberShip.ValidateUser(username,password);
if (validate)
{
// CxIdentity extends and implements IIdentity
var identity = new CxIdentity(memberShip.LoginStatus.SecurityUser.Name,
memberShip.LoginStatus.SecurityUser.Id,
"",
true);
this.Session[this.memberShip.PrincipalSessionKey] = new CxPrincipal(identity, memberShip.LoginStatus.SecurityUser.RoleId);
this.Session[SessionName.CurrentUser] = memberShip.LoginStatus.SecurityUser.UserNm;
FormsAuthentication.SetAuthCookie(username, false, "MyApp/Cookies");
loginResult = true;
}
}
catch (Exception ex)
{
Logger.Error("Login failed: ", ex);
}
if(loginResult)
return Redirect("~/Home");
else
return Redirect("~/Login");
}
And the following is the code of Application_AcquireRequestState, prior to when I tried to merge it
protected void Application_AcquireRequestState(object sender, EventArgs e)
{
if (HttpContext.Current.Handler is IRequiresSessionState)
{
if (Request.IsAuthenticated)
{
if (HttpContext.Current.Session[this.MemberShipProvider.PrincipalSessionKey] != null)
{
CxPrincipal principal;
try
{
principal = (CxPrincipal)HttpContext.Current.Session[this.MemberShipProvider.PrincipalSessionKey];
}
catch
{
principal = null;
}
HttpContext.Current.User = principal;
Thread.CurrentPrincipal = principal;
}
else
{
if (User.Identity.IsAuthenticated && User.Identity is FormsIdentity)
{
FormsAuthentication.SignOut();
Response.Redirect("~/Login");
}
}
}
}
}
The application was written is such a way that if the User isn't Authenticated the applciation will go drectly to Login page. So I changed the Application_AcquireRequestState as follows:
protected void Application_AcquireRequestState(object sender, EventArgs e)
{
if (HttpContext.Current.Handler is IRequiresSessionState)
{
var sessioncheck = this.Session[SessionName.CurrentUser];
var obj = this.Session[this.MemberShipProvider.PrincipalSessionKey];
if (obj != null)
{
HttpContext.Current.User = (CxPrincipal)obj;
Thread.CurrentPrincipal = (CxPrincipal)obj;
}
else
{
this.MemberShipProvider.ValidateUser("appadmin", "#dmin##!8854");
var identity = new CxIdentity("appadmin", 1, "", true);
CxPrincipal principalLogin = new CxPrincipal(identity, 1);
this.Session[this.MemberShipProvider.PrincipalSessionKey] = principalLogin;
this.Session[SessionName.CurrentUser] = "appadmin";
FormsAuthentication.SetAuthCookie("appadmin", false, "MyApp/Cookies");
}
if (Request.IsAuthenticated)
{
if (this.Session[this.MemberShipProvider.PrincipalSessionKey] != null)
{
CxPrincipal principal;
try
{
principal = (CxPrincipal)this.Session[this.MemberShipProvider.PrincipalSessionKey];
}
catch
{
principal = null;
}
HttpContext.Current.User = principal;
Thread.CurrentPrincipal = principal;
}
}
}
}
The problem is that everytime I check obj, it is always null. The Request.IsAuthenticated also gets true and while the application is within the acquire state the session does get set (that is why I get values in HttpContext.Current.User and Thread.CurrentPrincipal.
But when I redirect to another page, the session is gone.
Can anyone guide me through this.

Related

User.IsInRole() is not working in load balancer

Currently, I am working on a web application, based on the roles we have to display the controls in the UI. Roles are stored in the DB, whenever the user logs in, by fetching the user Id, I will hit the DB and get the user role and store it in the cookie. So, for the next request, I will fetch the user role from the User.IsInRole() and proceed with the logic same will happens in the view. This entire thing is working fine with the single server but when it comes to load balancer, this behaving weirdly and intermittently it's giving issue, as User.IsInRole() is returning false sometime.
The code in my controller:
public ActionResult IsValidUser(string userName)
{
try
{
if (HttpContext!=null&& HttpContext.Request.Headers["username"] != null)
{
userName = HttpContext.Request.Headers["username"];
}
//Get the roles by sending the user name
userRole = _processor.GetUserRole(userName);
UserViewModel user = new UserViewModel()
{
UserName = userName,
Role = userRole
};
if (!string.IsNullOrEmpty(user.Role))
{
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, user.UserName, DateTime.Now, DateTime.Now.AddMinutes(2880), true, user.Role, FormsAuthentication.FormsCookiePath);
string hash = FormsAuthentication.Encrypt(ticket);
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, hash);
if (ticket.IsPersistent)
{
cookie.Expires = ticket.Expiration;
}
if(Response!=null)
Response.Cookies.Add(cookie);
if (!string.IsNullOrEmpty(Request.Form["ReturnUrl"]))
{
return View("Error");
}
else
{
return RedirectToAction("Index");
}
}
else
{
return View("Error")
}
}
else
return RedirectToAction("Search");
}
catch (Exception ex)
{
return View("Error);
}
}
The code in Global.asax.cs
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
if (HttpContext.Current.User != null)
{
if (HttpContext.Current.User.Identity.IsAuthenticated)
{
if (HttpContext.Current.User.Identity is FormsIdentity)
{
FormsIdentity id = (FormsIdentity)HttpContext.Current.User.Identity;
FormsAuthenticationTicket ticket = id.Ticket;
string userData = ticket.UserData;
string[] roles = userData.Split(',');
HttpContext.Current.User = new GenericPrincipal(id, roles);
}
}
}
}
the code in my controller to do the logic:
public FileContentResult ViewAttachment(string attachmentName, int attachmentId)
{
if (ConfigurationManager.AppSettings["Environment"] != Constants.ProductionEnvironment ||
(User.IsInRole(Constants.Administrator) || User.IsInRole(Constants.Contributor) || User.IsInRole(Constants.Member)))
{
//Logic here
return File(bytes, mimeType);
}
else
{
_logger.WriteInformation("Not authorized");
return File(bytes, mimeType);
}
}
I am not sure what mistake is there but this is not working in load balancer sometimes it is showing "User is not authorized" but in actual user is authorized. Is it because of cookies or load balancer? Any help would be appreciated. Thanks in advance.

Custom ASP.NET MVC Forms Authentication

I want to do custom authentication because we have many controllers and it makes sense to create global filter that applies for all controllers and their actions with exception of login page.
In Global.asax.cs I added next global filter:
public class Global : HttpApplication
{
void Application_Start(object sender, EventArgs e) // Code that runs on application startup
{
... // only showing important part
GlobalFilters.Filters.Add(new Filters.AuthenticationUserActionFilter());
...
}
File AuthenticationUserActionFilter.cs:
public class AuthorizeUserActionFilter : System.Web.Mvc.Filters.IAuthenticationFilter
{
public void OnAuthentication(AuthenticationContext filterContext)
{
bool skipAuthorization = filterContext.ActionDescriptor.IsDefined(typeof(AllowAnonymousActionFilter), inherit: true) || filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(AllowAnonymousActionFilter), inherit: true);
if (skipAuthorization) // anonymous filter attribute in front of controller or controller method
return;
// does this always read value from ASPXAUTH cookie ?
bool userAuthenticated = filterContext.HttpContext.User.Identity.IsAuthenticated;
if (!userAuthenticated)
{
filterContext.Result = new RedirectToRouteResult(new System.Web.Routing.RouteValueDictionary() { { "controller", "Account" }, { "action", "Login" } });
return;
}
if( HttpContext.Current.User as Contracts.IUser == null )
{
// check if IUser is stored in session otherwise retrieve from db
// System.Web.HttpContext.Current.User is reseted on every request.
// Is it ok to set it from Session on every request? Is there any other better approach?
if (HttpContext.Current.Session["User"] != null && HttpContext.Current.Session["User"] as Contracts.IUser != null)
{
HttpContext.Current.User = HttpContext.Current.Session["User"] as Contracts.IUser;
}
else
{
var service = new LoginService();
Contracts.ISer user = service.GetUser(filterContext.HttpContext.User.Identity.Name);
HttpContext.Current.Session["User"] = user;
HttpContext.Current.User = user;
}
}
}
public void OnAuthenticationChallenge(AuthenticationChallengeContext filterContext) {}
}
My login code is like this (in AccountController.cs):
[Filters.AllowAnonymousActionFilter]
[HttpPost]
public JsonResult Login(string username, string password, bool rememberMe = false)
{
LoginService service = new LoginService();
Contracts.IUser user = service .Login(username, password);
System.Web.HttpContext.Current.Session["User"] = value;
System.Web.HttpContext.Current.User = value;
// set cookie i.e. ASPX_AUTH, if remember me, make cookie persistent, even if user closed browser
if (System.Web.Security.FormsAuthentication.IsEnabled)
System.Web.Security.FormsAuthentication.SetAuthCookie(username, rememberMe);
return new SuccessResponseMessage().AsJsonNetResult();
}
Contracts.IUser interface:
public interface IUser : IPrincipal
{
Contracts.IUserInfo UserInfo { get; }
Contracts.ICultureInfo UserCulture { get; }
}
My question is this:
System.Web.HttpContext.Current.User is reseted on every request. Is it ok to set HttpContext.Current.User with Session value on every request? Is there any other better approach? What is best practise? Also Microsoft seems to have multiple ways of dealing with this problem (googled a lot of articles on this, also on stackoverflow Custom Authorization in Asp.net WebApi - what a mess?). There is a lot of confusion about this, although they developed a new Authorization in asp.net core.
One possible approach is to serialize the user as part of the UserData portion of the ASPXAUTH cookie. This way you don't need to fetch it from the database on each request and you don't need to use Sessions (because if you use sessions in a web-farm you will have to persist this session somewhere like in a database, so you will be round-tripping to the db anyway):
[Filters.AllowAnonymousActionFilter]
[HttpPost]
public JsonResult Login(string username, string password, bool rememberMe = false)
{
LoginService service = new LoginService();
Contracts.IUser user = service.Login(username, password);
string userData = Serialize(user); // Up to you to write this Serialize method
var ticket = new FormsAuthenticationTicket(1, username, DateTime.Now, DateTime.Now.AddHours(24), rememberMe, userData);
string encryptedTicket = FormsAuthentication.Encrypt(ticket);
Response.Cookies.Add(new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket));
return new SuccessResponseMessage().AsJsonNetResult();
}
And then in your custom authorization filter you could decrypt the ticket and authenticate the user:
public void OnAuthentication(AuthenticationContext filterContext)
{
... your stuff about the AllowAnonymousActionFilter comes here
var authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie == null)
{
// Unauthorized
filterContext.Result = new RedirectToRouteResult(new System.Web.Routing.RouteValueDictionary() { { "controller", "Account" }, { "action", "Login" } });
return;
}
// Get the forms authentication ticket.
var authTicket = FormsAuthentication.Decrypt(authCookie.Value);
Contracts.ISer user = Deserialize(authTicket.UserData); // Up to you to write this Deserialize method -> it should be the reverse of what you did in your Login action
filterContext.HttpContext.User = user;
}

Authorize Attribute working unexpectively

I have extended the Authorize attribute to include roles which comes from a cookie. Debugging gives good result, it returns true or false accordingly. However if I first log in with "Admin" Role and then try to go to a controller that requires a User role, the Authorize returns false but still the controller allows access.
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext == null) throw new ArgumentNullException("httpContext");
if (httpContext.User != null)
{
if (httpContext.User.Identity.IsAuthenticated)
{
if (httpContext.User.Identity is FormsIdentity)
{
FormsIdentity id = httpContext.User.Identity as FormsIdentity;
FormsAuthenticationTicket ticket = id.Ticket;
string role = ticket.UserData;
if (RequiredRole.Contains(role)) return true;
}
}
else
return false;
}
return false;
}
Requiredrole is a property of the class.
[CustomAuthorize(RequiredRole = "Admin", LoginPage = "Club")]
public class UsuarioAdminController : Controller
{
above code for a controller that requires admin role.
[CustomAuthorize(RequiredRole = "User", LoginPage = "Club")]
public class HotelController : Controller
{
above code for a controller with User role.
Can someone see why if Authorize returns false it allows access? Thanks
The AuthorizeCore Attribute behave as expected, it returns true or false; however the controller allows access when the AuthorizeCore method returns false.
Yes, there is more code, but I dont think it makes a differnce..here it is.
public class CustomAuthorizeAttribute: AuthorizeAttribute
{
public string RequiredRole;
public string LoginPage;
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext == null) throw new ArgumentNullException("httpContext");
if (httpContext.User != null)
{
if (httpContext.User.Identity.IsAuthenticated)
{
if (httpContext.User.Identity is FormsIdentity)
{
FormsIdentity id = httpContext.User.Identity as FormsIdentity;
FormsAuthenticationTicket ticket = id.Ticket;
string role = ticket.UserData;
if (RequiredRole.Contains(role)) return true;
}
}
else
return false;
}
return false;
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
var routeValues = new RouteValueDictionary();
if (LoginPage == "Club")
{
routeValues["action"] = "Index";
routeValues["controller"] = LoginPage;
routeValues["ReturnUrl"] = filterContext.HttpContext.Request.RawUrl;
filterContext.Result = new RedirectToRouteResult(routeValues);
}
else {
routeValues["area"] = "mobile";
routeValues["action"] = "login";
routeValues["controller"] = LoginPage;
routeValues["ReturnUrl"] = filterContext.HttpContext.Request.RawUrl;
filterContext.Result = new RedirectToRouteResult(routeValues);
}
}
}
}
I found the answer to this particular situation and I am sharing my findings. When a request was properly unauthorized it was handled by the
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
method. Inside that method I was checking first if the user is not authenticated. For the first request it works properly sending the user to the appropriate log in page, But now if you try to navigate to another page that requires a different role, because you are already authenticated it wont go inside the redirect and will allow access to the control. So by removing the !IsAuthenticated if at the beginning, now all unauthorized request is properly sent to the correct login page...

Not able to delete the cookies on logout

//Interface--Injecting into controller
public interface IContext
{
HttpRequestBase Request { get; }
HttpResponseBase Response { get; }
}
//Implementation
public class Context : IContext
{
public HttpRequestBase Request { get { return new HttpRequestWrapper(HttpContext.Current.Request); } }
public HttpResponseBase Response { get { return new HttpResponseWrapper(HttpContext.Current.Response); } }
}
//Setting Cookie on login.
SetCookie(_context, login);
public void SetCookie(IContext context, Login login)
{
var loginDetails = new JavaScriptSerializer().Serialize(login);
var cokie = new HttpCookie("login", login);
context.Response.Cookies.Add(cokie);
}
//Trying to Logout.
public ActionResult Logout()
{
foreach (var key in _context.Request.Cookies.AllKeys)
{
try
{
_context.Response.Cookies.Remove(key); //this didn't work
//tried this even this is not working
var login = new Login {Identity = "", LogIn = false};
_login.SetCookie(_context, login);
}
catch (Exception e)
{
}
}
_context.Response.Cookies.Clear(); //this didn't work either
return RedirectToAction("Index", "Login");
}
On Login Index when I check the current login cookie value it always has the value of the logged in user is just not getting set to null or empty.
I suspect there is something wrong in the Implementation of the IContext. Should there be a setter on it? Not sure..
Also Tried:
var cokie = context.Request.Cookies["login"];
cokie.Expires = DateTime.Now.AddDays(-2);
cokie.Value = null;
context.Response.Cookies.Add(cokie);
You can use the below code if you are using forms authentication. It will clear all the required cookies
FormsAuthentication.SignOut();
Session.Abandon();
Alternatively you can use
Session.Abandon();
Response.Cookies.Clear();
or
YourCookies.Expires = DateTime.Now.AddDays(-1d);
For more information please visit
MSDN Cookie Help
If to log user in you use
SignInManager.PasswordSignInAsync(...)
you can try
AuthenticationManager.SignOut();
instead of
_context.Response.Cookies.Clear();

ASP.NET MVC4 c# : Get user roles

This is the code I used for membership in Global.asax
protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
{
if (FormsAuthentication.CookiesSupported == true)
{
if (Request.Cookies[FormsAuthentication.FormsCookieName] != null)
{
try
{
//let us take out the username now
string username = FormsAuthentication.Decrypt(Request.Cookies[FormsAuthentication.FormsCookieName].Value).Name;
string roles = string.Empty;
IUserService _userService= new UserService();
UserViewModel user = _userService.SelectUserByUserName(username).UserList.FirstOrDefault();
roles = user.role;
//let us extract the roles from our own custom cookie
//Let us set the Pricipal with our user specific details
HttpContext.Current.User = new System.Security.Principal.GenericPrincipal(
new System.Security.Principal.GenericIdentity(username, "Forms"), roles.Split(','));
}
catch (Exception)
{
//somehting went wrong
}
}
}
}
I'm trying to redirect the user for different view if his Role is "Manager",this is what I tried to get the user roles in the controller but It returns an empty list :
[Authorize(Roles = "admin, manager")]
public ActionResult Index()
{
string[] rolesArray;
rolesArray = Roles.GetAllRoles();// returns an empty array
foreach(var item in rolesArray){
if(item == "manager"){
return RedirectToAction("index", "Manager");
}
}
return View();
}
You should be able to call .IsInRole()
if (User.IsInRole("manager"))
{
return RedirectToAction("index", "Manager");
}

Categories

Resources