I have created my own customization for the AuthorizeAttribute inside my asp.net mvc web application, and to be able to return the user to the current URL after login , i am trying to save the current URL inside a TempData and then redirect to the login action method ,as follow-
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (!_authorize && !filterContext.HttpContext.Request.IsAjaxRequest())
{
var viewResult = new RedirectResult("/Account/Login");
TempData["returnUrl"] = filterContext.HttpContext.Request.Url.PathAndQuery;
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
filterContext.Result = viewResult;
}
but seesm that i can not reference TempData in this case, because the above code will raise the following error:-
The name 'TempData' does not exist in the current context
Can anyone advice please?
Thanks
Try using controller base,
filterContext.Controller.TempData["returnUrl"] = filterContext.HttpContext.Request.Url.PathAndQuery;
Also TempData may behave unexpectedly in Authorize attribute as it lives only on one request cycle. If so use Session instead.
Related
I'm new to MVC (5). In order to add localization support to my website I added a "Language" field to my ApplicationUser : IdentityUser
What's the best approach to now store this information in the browser and ensure that it gets re-created even if the user manually deletes it?
TL; but I've got time
What I've tried until now:
I started creating a cookie in my method private async Task SignInAsync(ApplicationUser user, bool isPersistent) but I notice that:
This method is not used if the user is already authenticated and automatically logs in using the .Aspnet.Applicationcookie and my language cookie could be meanwhile expired (or been deleted).
A user could manually delete the cookie, just for fun.
I thought about checking its existence in the controller (querying the logged user and getting it from the db) and it works but I'd need to do it in EVERY controller. I'm not sure is the correct way to do this.
Any suggestion about how to approach this problem and guarantee that the application has a valid "language cookie" on every request?
It sounds to me like what you want here is a Custom Action Filter. You can override the OnActionExecuting method which means the logic is run before any action is called
public class EnsureLanguagePreferenceAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var langCookie = filterContext.HttpContext.Request.Cookies["LanguagePref"];
if (langCookie == null)
{
// cookie doesn't exist, either pull preferred lang from user profile
// or just setup a cookie with the default language
langCookie = new HttpCookie("LanguagePref", "en-gb");
filterContext.HttpContext.Request.Cookies.Add(langCookie);
}
// do something with langCookie
base.OnActionExecuting(filterContext);
}
}
Then register your attribute globally so it just becomes the default behaviour on every controller action
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
filters.Add(new EnsureLanguagePreferenceAttribute());
}
To me, the easiest way would be to create your own Authorize attribute (since your language options are tied to an authenticated user account). Inside of your new authorize attribute, simply perform the check if the cookie exists. If it does, then life is good. Else, query the user's database profile and reissue the cookie with the stored value
public class MyAuthorization : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
//no point in cookie checking if they are not authorized
if(!base.AuthorizeCore(httpContext)) return false;
var cookie = httpContext.Request.Cookies["LanguageCookie"];
if (cookie == null) {
CreateNewCookieMethod();
}
return true;
}
}
To use, replace [Authorize] with [MyAuthorization] in your project.
If you don't want to mess with the [Authorize] attribute, you could create your own attribute that does the cookie checking and decorate your controller with that one as well.
One last alternative is to create your own Controller class that does the checking on the OnActionExecuting.
public class MyBaseController : Controller
{
public string Language {get;set;}
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
var cookie = filterContext.HttpContext.Request.Cookies["LanguageCookie"];
if(cookie == null){
cookie = CreateNewCookieMethod();
filterContext.HttpContext.Request.Cookies.Add(cookie);
}
Language = cookie.Value;
base.OnActionExecuting(filterContext);
}
How to use (note that we inherit from MybaseController now)
public class HomeController : MyBaseController{
public ActionResult Index(){
//Language comes from the base controller class
ViewBag.Language = Language;
Return View();
}
}
This method is neat because now that Language variable will be available in any controller that inherits from this new class.
Either of these will give you a single, cookie checking point. Additionally, you are only going back to the database only in the instance that the cookie does not exist.
Is there any way to access session on view page by creating common method in controller also want to access session in controller by common method in ASP.Net MVC.
are you using razor view engine?
View:
#{ var sessionVar = Session["key"]; //it's object }
Controller:
public ActionResult Method() {
var sessionVar = this.Session["..."]; //
}
the common way to call this object is: HttpContext.Current.Session
I don't know what you meant by 'common'. the provided session object is common for user session no matter where you will call for it.
But in fact you shouldn't try to use session it's ugly - try to do some search about ViewBag / ViewData and then try to search why you shouldn't use them as well. :)
Am new to MVC but I think I know what you're trying to do. You don't need to create a 'common' method to achieve that. #trn Solution should work but it might throw Null reference exceptions if accessed in "View" without using "HttpContext.Current.Session" object for two possible reasons:
Session["key"] is null.
incorrect reference to Session["key"] in View and/or Controller.
Using "HttpContext.Current.Session" object is great way to achieve what you're looking for and make sure the Session["key"] is not null. For example:
View:
#{
var sessionVar = Session["key"]; // might throw a NullReferenceException
}
Alternative for View:
#{
var sessionVar = HttpContext.Currrent.Session["key"]; // assuming correct reference to Session["key"]
}
Controller:
Public ActionResult someMethod()
{
var sessionVar = System.Web.HttpContext.Current.Session["key"];
}
I'm developing a simple test website using ASP.NET MVC3 and Razor syntax. It has _LayoutPage.cshtml as master template which uses #HTML.Action to print user id at the top of the site for each page.
I implemented a childAction named userInfo for this partial view which reads the user id from HTTPContext.Session and prints it out. The child action is implemented in a controller called CommonActionController derived from Controller. In addition to user id it also reads two more variables from session and prints it.
public class CommonActionController: Controller
{
[ChildActionOnly]
public ActionResult userInfo()
{
if(HTTPContext.Session["x-user-id"] != null)
{
ViewBag.UserId = (string)(HTTPContext.Session["x-user-id"]);
ViewBag.UserFirstName = (string)(HTTPContext.Session["x-user-first-name"]);
ViewBag.UserLastName = (string)(HTTPContext.Session["x-user-last-name"]);
ViewBag.UserLoggedinSince = (DateTime)(HTTPContext.Session["x-user-logon-timestamp"]).ToString("f");
}
return PartialView();
}
}
My main page controller called HomeController has the dashboard functionality implemented in Dashboard action (currently it just prints the word "Dashboard"). In this controller I have overridden Controller.OnActionExecuting() method which validates that the user id exists in session. It reads total three variables from session just like the aforementioned childAction.
public class HomeController: Controller
{
public HomeController()
{
}
protected override void OnActionExecuting(ActionExecutingContext ctx)
{
base.OnActionExecuting(ctx);
if(HTTPContext.Session["x-user-id"] == null)
ctx.Result = new RedirectResult("logon/userlogon");
if(HTTPContext.Session["x-user-logon-timestamp"] == null)
ctx.Result = new RedirectResult("logon/userlogon");
if(HTTPContext.Session["x-user-internal-flag"] == null)
ctx.Result = new RedirectResult("logon/userlogon");
}
public ActionResult Dashboard()
{
// nothing to see here
return View();
}
}
I have cleaned up the code little bit to remove the debug.print statements.
As per the logs I see that the OnActionExecuting() method and userInfo child action are invoked simultaneously. At one point OnActionExecuting() gets nulls for session variables. In the log I can see that until the point ChildAction is invoked, session variables hold their value within OnActionExecuting(). Once the childaction accesses them, they become null.
When I comment the code that accesses session from child action, everything works fine. What am I doing wrong? Is there some precaution I have to take while accessing session variables? Is this due to my ignorance about how to access Session asynchronously?
I also have following in my web.config:
<modules runAllManagedModulesForAllRequests="true"/>
1) Start–> Administrative Tools –> Services
2) right click over the ASP.NET State Service and click “start”
*additionally you could set the service to automatic so that it will work after a reboot.
For more details you can check my blog post: http://jamshidhashimi.com/2011/03/16/unable-to-make-the-session-state-request-to-the-session-state-server/
ref:
Unable to make the session state request to the session state server
I have searched all over the internet and SO, and I have found some good stuff on this topic, but I have a few questions that I am still unsure about:
1) I am using Forms Authentication with a custom Authentication provider. So I use the Authorize attribute and the section in the web.config still, but basically when the FormsAuthenticationTicket does not exist, I redirect to a login page (specified in the web.config) which then utilizes the custom Authentication Provider to auth the user against a db and then issues the FormsAuthenticationTicket. Is this correct?
2) Should I be using a custom Authorize attribute or should I just inject a GenericPrincipal into the HttpContext from the Application_AuthenticateRequest event handler in the global.asax page? Or should I be using User.IsInRole insode of the controller actions?
I just need role based authorization, and I think my Authentication Scheme is pretty good.
Any pointers/advice?
Thanks,
Sam
Edit
So from what I have read, the best option for this is to create a custom AuthorizeAttribute and override the AuthorizeCore.
So what I have done is this:
public class CustomAuthorize : System.Web.Mvc.AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext.User.Identity.IsAuthenticated)
{
var model = AdminUserViewModel.FromJsonString(((FormsIdentity)httpContext.User.Identity).Ticket.UserData);
httpContext.User = new GenericPrincipal(HttpContext.Current.User.Identity, model.SecurityGroups.Select(x => x.Name).ToArray());
}
return base.AuthorizeCore(httpContext);
}
protected override void HandleUnauthorizedRequest(System.Web.Mvc.AuthorizationContext filterContext)
{
//base.HandleUnauthorizedRequest(filterContext);
filterContext.Result = new System.Web.Mvc.RedirectResult("/Authentication/NotAuthorized", false);
}
}
Simply inject a new principal/identity with the roles that are stored in the FormsAuthenticationTicket UserData property. Then let the base do the rest.
Does this seem to be OK?
Edit #2
I am a little weary of using the Application_AuthenticateRequest in the global.asax with IIS7, because of the integrated pipeline, every request fires that event, images, css, js...
Is this correct?
1) I do the same thing.
2) I use Authorize attribute and Application_AuthenticateRequest event handler.
In Application_AuthenticateRequest event handler I do something like this:
string[] roles = authenticationTicket.UserData.Split(',');
if (Context.User != null)
Context.User = new GenericPrincipal(Context.User.Identity, roles);
And at controller or action level I do something like this:
[Authorize(Roles = "Admin, SuperAdmin")]
I have 2 projects in my solution.
MVC Web application
Class library
The MVC Web application references the class library.
The class library contains a class that extends the default ASP.Net Controller.
I'm putting a variable in session in the application's Global.asax.
protected void Session_Start(object sender, EventArgs args)
{
HttpContext.Current.Session["DomainName"] = Request.Url.Host;
}
In the class library I'm trying to get the value from the HttpContext.Session, but HttpContext.Session keeps coming up null.
public class MyController : System.Web.Mvc.Controller
{
public MyController () : base()
{
//HttpContext.Session is always null at this point
ViewData["DomainName"] = HttpContext.Session["DomainName"];
}
}
HttpContext.Current.Session doesn't seem to be an option in controllers. Any ideas?
Two issues -- the HttpContext property in the Controller class is the current session. Unfortunately, it's not available in the constructor of the controller. Obviously because it's not passed in the constructor, it has to be set via the property afterwards. You might consider adding a property to hold the domain name and referencing the session from it -- that way it would be available for use when needed.
protected string DomainName
{
get { return this.HttpContext.Session["DomainName"] as string; }
}
The set it in ViewData in your actions or in OnActionExecuting/OnActionExecuted.
protected override void OnActionExecuted( ActionExecutedContext context )
{
ViewData["DomainName"] = this.HttpContext.Session["DomainName"];
// or ViewData["DomainName"] = this.DomainName; // if you used the property
}
If you're just trying to add ViewData from the session, try doing it in the OnActionExecuting method. This is where I typically add ViewData I want for every View.
You just use Session by itself (it's a property of Controller), but that just maps to Controller.HttpContext.Session (in other words, what you're already using), so it won't solve your problem, which must be elsewhere.
I'm not sure why you're putting this in the Session, though, as you can read Request.Url.Host directly during the Action.
When you create cookie then you must write
Response.AppendCookie("Your cookie name");
And if you want to get that then something like this
if (Request.Cookies["Your cookie name"] != null)
{
string value = Request.Cookies["Your cookie name"].Value;
}
and must if there are different solutions
then
machineKey
need to be same which is under
system.web
in web.config and then write
<httpCookies domain=".yourdomainname.com" />