The following code snippet is from my ASP.NET MVC 5 application:
public ActionResult Ask(string id) {
if (!this.User.Identity.IsAuthenticated) {
string retUrl = Request.Url.AbsoluteUri;
return RedirectToAction("Login", "Account", new { returnUrl = retUrl });
}
...
}
The idea is, if the user has not yet logged in, he will be taken to the login page and subsequently be returned back to this "Ask" page.
When the user enters, for example, http://example.com/Home/Ask/12345678, method Ask() gets invoked with the correct value for id. The user is now redirected to the login page.
After Login() code in AccountController successfully authenticates the user, it calls ReturnToLocal(), passing in the url that we expect (http://example.com/Home/Ask/12345678). However, instead of invoking the Ask() method , ASP .NET somehow ends up invoking Index() method.
Appreciate your help in understanding why my redirection is broken. Regards.
Since you did not provide us neither the Login action method nor RedirectToLocal method I am assuming you are using the AccountController from MVC5 template.
If so
private ActionResult RedirectToLocal(string returnUrl)
{
if (Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
return RedirectToAction("Index", "Home");
}
You can see that RedirectToLocal method checkes if the returnUrl parameter is 'localUrl'.
If not it does invoke Index.
In your Ask method you are passing string retUrl = Request.Url.AbsoluteUri as returnUrl which is not local (starts with http://)!
Try string retUrl = Request.Url.PathAndQuery
Related
I'm using returnUrl to return to the last requested page when logging in after being logged out due to inactivity. In itself this works fine, unless the user does not requests a page, but some other action. What I would like is that in case the first action after SessionTimeOut is for example "addItem", the returnUrl is set as "controller/index" instead of "controller/addItem".
After SessionTimeOut the application does not automatically redirect to the login page. The redirect happens as soon as the user makes a new request after SessionTimeOut (as requested by client). So when the user clicks a menu item, the returnUrl is set to the requested page. Great so far.
The issue is though when the user click for example the addItem button that the returnUrl is set as the action method linked to that button. I don't understand how I can exclude this kind of methods from returnUrl.
This is a very scaled down example of the application, to give an idea of how it hangs together.
MainController:
public ActionResult MainPage(){
return View();
}
public ActionResult addItem(){
--- Do Something ---
}
public ActionResult SecondPage(){
return View();
}
Login method:
public ActionResult Login([Binde(Include = "UserName, Password")]LoginModel model, string returnUrl)
{
if(Modelstate.IsValid)
{
--- Do Validation ---
return Redirect(returnUrl);
}
}
In case the user is on the MainPage and makes after SessionTimeOut the request for "SecondPage", the returnUrl should be and is "controller/SecondPage".(OK)
In case the suer is on the MainPage and makes after SessionTimeOut the request for "addItem", returnUrl should be "controller/MainPage" and not "controller/addItem"
One quick solution is to put Action Filters into effect.
As you might know, an action filter is an attribute that you can apply to a controller action -- or an entire controller -- that modifies the way in which the action is executed. More details, see documentation here
In this case, you need to override OnActionExecuting method, is called before a controller action is executed, so that prior to execution, you can decide to redirect or not based on the return value.
I provide you with a simple example :
public class myCheckUrlActionFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
List<string> avoidList = new List<string> { "addItem", "removeItem"};
foreach(string i in avoidList)
{
if(filterContext.HttpContext.Request.Url.Query.Contains(i))
{
filterContext.Result = new RedirectResult("/Home/MainPage") ;
break;
}
}
}
}
Now, Let's apply this filter to all action methods inside your Home Controller, though you may apply it individually to only certain action methods.
[myCheckUrlActionFilter]
public class HomeController : Controller
{ .... }
Access to HttpContext inside the body of the filter, enables to extract session, and even current controller and action name. Therefore, you can check session state here in addition returnUrl existence to further decide on redirect. So, customize it as you need.
Hope this helps.
I have two Controllers TokkenIssuer and Login.
I have used [authorize] for a method A in TokkenIssuer which takes to Login controller's view and then to Login controller's Login action.
Login Controller:
public ActionResult Login(string user,string password,string returnUrl)
{
FormsAuthentication.SetAuthCookie(user, true);
if (FormsAuthentication.Authenticate(user, password))
{
if (string.IsNullOrEmpty(returnUrl) && Request.UrlReferrer != null)
returnUrl = Server.UrlEncode(Request.UrlReferrer.PathAndQuery);
return RedirectToAction("B", "TokenIssuer");
}
return View();
}
From Login i am redirectingtoaction B in Tokenissuer Controller.
I came to know User.Identity.IsAuthenticated will becomet true after a redirect to another controller but i am unable to fetch User object nor find the User.Identity.IsAuthenticated as true in my redirected controller (TokenIssuer).
TokenIssuer Controller
[Authorize]
public ActionResult A() //takes to Login view
{
return View();
}
public ActionResult B() //Redirected from Login controller
{
if(User.Identity.IsAuthenticated) // Getting False
{
string user = User.Identity.Name;
}
}
how to resolve this issue?
It will be false until the next request.
Here's a good explanation: http://forums.asp.net/t/1177741.aspx
In short, the User object is set earlier in the ASP.NET pipeline, long before the requested ASP.NET page's code is executed. Now, on the subsequent visit, the ASP.NET runtime will see the forms authentication ticket and User.Identity.IsAuthenticated will be true, but not on this request.
Reference: https://www.experts-exchange.com/questions/28063988/User-Identity-IsAuthenticated-is-false-after-a-successful-Login-Validation.html
It took a lot of time to find the issue.
I came to know, In VS 2013 or above when we create a web application by default it adds the following code in web.config file which disables the form / claim based authentication to create the user object.
<module>
<remove name = "FormsAuthentication"/>
</module>
So Comment or Remove.This helped me.
I have a requirement to implement Back button functionality in MY MVC2 application(with explicit Back buttons in the application, not via browser back button).
I have gone through the link here Back button functionality using ASP.net MVC which advises to use Request.UrlReferrer. and it works well whenever I have a HTTP Get (as it comes up with querystring.) but I face issue when previous page was a HTTP POST with no querystring.
Has anyone worked on a solution like this?
We have initial thoughts of generating and storing links like Stack, and popping out urls one by one when user clicks on 'Back' button. But if there is a better solution\design pattern which helps me achieve that?
You could store the returnurl to actionresult routevalues and have a html helper for back link.
Helper:
public static class HtmlHelpersExtensions
{
public static MvcHtmlString BackLink(this HtmlHelper helper,string defaultAction)
{
string returnUrl = HttpContext.Current.Request["returnUrl"];
// If there's return url param redirect to there
if (!String.IsNullOrEmpty(returnUrl))
{
return new MvcHtmlString("<a href=" + returnUrl + " >Back</a>");
}
// if user just posted and there's no returnurl, redirect us to default
else if (HttpContext.Current.Request.HttpMethod == "POST")
{
return helper.ActionLink("Back", defaultAction);
}
// we didn't post anything so we can safely go back to previous url
return new MvcHtmlString("<a href=" + HttpContext.Current.Request.UrlReferrer.ToString() + " >Back</a>");
}
}
Controller:
public ActionResult MyAction(string returnUrl)
{
return View(new MyModel());
}
[HttpPost]
public ActionResult MyAction(MyModel mm, string returnUrl)
{
if (ModelState.IsValid)
{
// No returnurl, redirect us to somewhere
if (string.IsNullOrEmpty(returnUrl))
return RedirectToAction("Index");
// redirect back
return Redirect(returnUrl);
}
return View(mm);
}
Usage:
#Html.ActionLink("MyAction", "MyAction", new { returnUrl = Request.RawUrl.ToString() })
Backlink in MyAction.cshtml
#Html.BackLink("Index")
Now the helper decides whether it uses returnUrl param, default action or takes you back via urlreferrer.
I am using Forms Authentication in my MVC 3 app and having a problem with my return URL.
When I mark an action <Authorize> on the Home controller, it redirects to the login page and works, but the return URL is then /, so when it redirects, it is redirecting to the root of the the current URL Authorize.
So the URL's are like this:
http://localhost/ - Controller = Home - Action = Index
http://localhost/Authentication/LogOn
I end up with this: http://localhost/Authentication/LogOn?ReturnURL=~%2F, I need to get back to http://localhost/
Help!! :)
Try changing your Account controllers LogOn action to something like this:
[HttpPost]
public ActionResult LogOn(LogOnModel model, string returnUrl)
{
if (ModelState.IsValid)
{
if (MembershipService.ValidateUser(model.UserName, model.Password))
{
FormsService.SignIn(model.UserName, model.RememberMe);
return RedirectToAction("Index", "Home");
}
else
{
ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
}
// If we got this far, something failed, redisplay form
return View(model);
http://localhost/Authentication/LogOn?ReturnURL=~%2F,
it means home url is duplicated
When implementing an MVC web app with FormsAuthentication, it would appear that the FormsAuthentication mechanism automagically appends ?ReturnUrl=/ to your mvc routes. I did notice, though, that the default web application which MVC 3 provides out of the box implements FormsAuthentication but does not appear to suffer from the appended ReturnUrl=/ problem. I've looked through the code and can't see where this is being handled. Anyone know?
In the LogOn action of the AccountController, you'll see that if a return url is present, the user will be redirected to it after a successful log on:
[HttpPost]
public ActionResult LogOn(LogOnModel model, string returnUrl)
{
if (ModelState.IsValid)
{
if (MembershipService.ValidateUser(model.UserName, model.Password))
{
FormsService.SignIn(model.UserName, model.RememberMe);
if (Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
else
{
ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
Try adding the [Authorize] attribute to the Action or controller. This will generate the url needed to redirect after login.