Where is value of "returnUrl" set in MVC 5? It always takes me to /Home/Index - authentication

It is null everywhere I find it during runtime.
I've got all the bells and whistles that halfway come with MVC 5 Template VS 2013.
I figured out all the extra Indentity and DataAnnotations stuff and it all works. (40 files and 5 tables later sheez).
But when signing in it all follows through but the registered user gets redirected to a "Home/Index" page, the default page that comes with template. I want them to get somewhere else, like "PickLocation".
Now nowhere in my code can I find "/Home/Index" referenced by searching the entire solution. In Web.config the authentication mode is set to "none".
Here is my Register link in _LoginPartial.cshtml:
<li>#Html.ActionLink("Register", "Register", "Account", routeValues: null, htmlAttributes: new { id = "registerLink", style = "color:white;" })</li>
Somehow it heads for the HomeController.
Here is the Home Controller ActionResult that I can put a breakpoint to prove it was hit:
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Notice = "This is coming from Home Controller";
return View();
}
In my AccountController the Login ActionResult returns "null" for the "returnURL" parameter. On Success (which is what happens) it goes to "return RedirectToLocal(returnUrl);" Where is this set? Nowhere can I find value of returnURL or where it is set and I don't want to continue to end up on the MS default Home/Index page that comes with the template. Log Out also goes to Home/Index page. I set IIS default page to "PickLocation.cshtml" but no luck
Here is the Login Action code here:
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (!ModelState.IsValid)
{
return View(model);
}
// This doesn't count login failures towards account lockout
// To enable password failures to trigger account lockout, change to shouldLockout: true
var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
switch (result)
{
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
Edit:
I just remembered someone is going to ask if this is set in RouteConfig.cs. .. .no it's not.
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
//routes.MapRoute(
// name: "Default",
// url: "{controller}/{action}/{id}",
// defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
//);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Stores", action = "PickLocation", id = UrlParameter.Optional }
);
}
Here is the AccountController Login GET "method"
// GET: /Account/Login
[AllowAnonymous]
public ActionResult Login(string returnUrl)
{
ViewBag.ReturnUrl = returnUrl;
return View();
}
But the question still stands: "where in the heck is the project/solution getting the "/home/index" that it sends me back to after registering or logging off? No where does the "/home/index" show up in a search.

Oh my gosh, if it was a snake it would have bit me.
I was searching for "Home/Index". I didn't think that it would be separate as in "Action, Controller". sheez.
This StackOverFlow question/answer showed me the light. I searched in my project for "private ActionResult RedirectToLocal" and I got this Action and down at the bottom was "return RedirectToAction("Index", "Home");" Pretty sure I can change it there. I hope my misery will help some other newby like me.
private ActionResult RedirectToLocal(string returnUrl)
{
if (Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
return RedirectToAction("Index", "Home");
}

Related

Redirect to Account/Login view in asp.net core 7, from an anonymous action method of a controller

Home/Index is my default route of asp.net core application. In this method "index" of controller "Home", I need to check if the user is signed in or not, if the user is signed in, the user will be redirected to a special page according to role of the user.
In other case, the user will be redirected to login page.
But login page is not being displayed and also not being shown any error message like the page is not found.
I tried following different options after studying articles in stackoverflow and other sites
return RedirectToAction("Login", "Account");
return RedirectToAction("Login", "Account", new {area = "Identity"});
return RedirectToAction("Login", "Account", new {area = ""});
return RedirectToAction("Login", "Identity/Account", new {area = ""});
return Redirect("/Account/Login");
But if applied [Authorize] attribute, then Account/Login page is being shown. So please guide me how to handle this scenario.
Following is given the complete flow and logic
public IActionResult Index()
{
if (_userManager.IsSignedIn(User))
{
return RedirectToAction("MyDetails", "SupAdmin");
}
else
{
return RedirectToAction("Login", "Account");
return RedirectToAction("Login", "Account", new {area = "Identity"});
return RedirectToAction("Login", "Account", new {area = ""});
return RedirectToAction("Login", "Identity/Account", new {area = ""});
return Redirect("/Account/Login");
}
}
I try the below code, then it displays the login view:
public IActionResult Index()
{
return RedirectToAction("Login", "Account");
}
i hope this is what you want to do, but since i think i was in the same issue as you here you go:
If you want to call, for example "Login.cshtml" or "Register.cshtml" from a own Controller (example: HomeController) you need to return the View() instead of redirecting to it. Here is some code for explanation:
[HttpPost]
public ActionResult Register()
{
return View("/Areas/Identity/Pages/Account/Register.cshtml");
}
This function gets Called when the user clicks a button by an Ajax.Post. After that redirects to Identity/Register View. Make sure to check the route of the view, yours might be different from mine.

How to link from View in Area to the root controller?

I'm new to MVC, still learning ins and outs by converting .NET App to MVC.
I have trouble linking to another Action in my controller from the page inside of the "Area" section.
I created Areas and still have the default route set up.
The Area has the following structure:
Area
- Admin
- Controller
UserController.cs
-Model
-Views
User
Index.cshtml
Index.cshtml has a link that should call "Index" action in AccountController to open "Default" view:
<div class="pagenavigator">#Html.ActionLink("Main Menu", "Index", new { area = "", controller="Account" })</div>
The default structure of the application is the following:
- Controller
AccountController.cs
- Views
- Account
Default.cshtml
Login.cshtml
My default structure has also controller folder that has a controller with Login (default) action set in RouteConfig.cs:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Account", action = "Login", id = UrlParameter.Optional }
);
Here is Account Controller:
public class AccountController : Controller
{
public ActionResult Login(string returnUrl)
{
StringBuilder sb = new StringBuilder();
sb.Append("Web Security Administration Portal<br/><br/>");
ViewBag.ReturnUrl = returnUrl;
ViewBag.Message = sb.ToString();
return View();
}
public ActionResult Index()
{
return View("Default");
}
public ActionResult Login(LoginModel model)
{
if (ModelState.IsValid)
{
bool authenticated = Security.AuthenticateLANUser(model.UserName, model.Password);
if (!authenticated)
{
Session["authenticated"] = false;
System.Text.StringBuilder errorMsg = new System.Text.StringBuilder();
errorMsg.Append("Invalid Login/Password entered.");
errorMsg.Append("We were not able to authenticate you in in Active Directory based on the information entered, ");
errorMsg.Append("but we recorded your attempt for audit purposes.");
ModelState.AddModelError("", errorMsg.ToString());
return View(model);
}
else
{
return View("Default");
}
}
ModelState.AddModelError("", "The user name or password provided is incorrect.");
return View(model);
}
}
What should I do if I need to link to "Default" view defined under default application route, from Index.cshtml defined under Area, so when I click the link, my "AccountController" gets called with the correct "Index" action?
In short, I need to find out the way to link from "Area" section to a controller's action in default application section, so the another correct Action gets called, which is not specified in default route mapping
Right now, the link is broken.
When I view the link in the source, I see the following: <a href="/Account/Index">, but when I click on the link, I'm getting the error saying: The resource cannot be found with Requested URL: /login.aspx
Here is AdminAreaRegistration:
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Admin_default",
"Admin/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional }
);
}

MVC5 EF6 How to add confirmation screen with additional authentication before submitting data

Developing a new MVC5 project. I have my scaffolding in place for CRUD functionality but there is a requirement that when data is inserted or updated, an e-signature is required. Before data can be submitted to the database the user must be presented with a page asking them to enter their username and password again to confirm the data. If the username and password entered is valid and the username matches the currently signed in user, then the original data entered can be saved to its table (for example Member) and the e-signature information is saved to a separate table (ESignature). I'd appreciate any help on the best way to go about this - a view model combining Member and ESignature, or a reuse of the LoginViewModel from the Account controller to check the authentication, or an alternative approach? I need something that I can use across half a dozen controllers where e-signatures are required.
Alright maybe my approach is not the best but I will attempt.
My solution would be to create a CustomAttribute: AuthorizeAttribute and decorate all the actions which require Esignature. In your CustomAttribute implementation you will redirect to a controller action exactly similar to Login but with slight modification.
public class CustomAuthorize : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
var url = filterContext.HttpContext.Request.Url;
var query = url.Query;
if (query.Contains("g="))
{
var code = query.Split(new String[] { "g=" }, StringSplitOptions.None);
//You can create time sensistive token and validate it.
}
else
{
//Redirect User to a particular page
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary
{
{ "controller", "Account" },
{ "action", "elogin" },
{ "redirectUrl", url.AbsolutePath}
}
);
}
}
}
Then decorate for example Index() method with it.
[CustomAuthorize]
public ActionResult Index()
{
return View();
}
At first when you hit the Index() method then inside OnAuthorization method of CustomAuthorizeAttribute the else loop gets executed and re-directs you to a elogin method inside AccountController. This method is similar to the Login HttpGet method. While specifying the RedirectToResult I am specifying the redirectUrl path of the current page so when you successfully validate a user inside the elogin method then with the help of redirectUrl we can come back.
[AllowAnonymous]
public ActionResult ELogin(string returnUrl)
{
ViewBag.ReturnUrl = returnUrl;
return View("Login");
}
//
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ELogin(LoginViewModel model, string returnUrl)
{
if (ModelState.IsValid)
{
var user = await UserManager.FindAsync(model.UserName, model.Password);
if (user != null)
{
await SignInAsync(user, model.RememberMe);
var url = String.Format("{0}/?g={1}", returnUrl, "HashCode");
return RedirectToLocal(url);
}
else
{
ModelState.AddModelError("", "Invalid username or password.");
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
The only difference in the HttpPost ELogin method is that before doing RedirectToLocal I append /g=HasCode. Note: Here you can append your own logic to create a time sensitive hash. When we get redirected to our home page then we can inspect inside our OnAuthorization Method if the url contains g=HashCode then don't redirect to Login Page.
This would be very basic idea on how you can approach to force users to re-sign in whenever they hit specific controllers. You will have to do additional security checks and be careful in what you are exposing via url.

Create a route with an additional url parameter without creating an area

I'm building a site for a client using .Net MVC 4. The entire site uses the default MVC 4 route and all the pages work fine
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
)
There is a page on the site called 'Teachers'. For this page, there are several links that take you to new pages that are subsets of the 'Teachers' page. The client wants the url structure to appear like this
www.{mysite}.com/School/Teachers/Apply
www.{mysite}.com/School/Teachers/Benefits
I thought I could simple add the Apply and Benefits pages as an ActionResult in my SchoolController then use the routing feature in MVC to map the url to the correct ActionResult method in the SchoolController.
This is my controller:
public class SchoolController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult Administration()
{
return View();
}
public ActionResult Teachers()
{
return View();
}
public ActionResult Apply()
{
return View();
}
public ActionResult Benefits()
{
return View();
}
}
This is the custom route that I tried.
routes.MapRoute(
"Teachers",
"{controller}/{page}/{action}",
new { controller = "School", page = "Teachers", action = "Index" }
)
I placed this route before the default route but this adds 'teachers' to every url on the site like this:
www.{mysite}.com/{controller}/teachers/{action}
SUMMARY
All the pages on my site use this url structure:
www.{mysite}.com/{controller}/{action}
This one page, however, has the following structure:
www.{mysite}.com/{controller}/teachers/{action}
How can I do this with routes?
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "TeachersActions",
url: "School/Teachers/{action}",
defaults: new { controller = "School" },
constraints: new { action = "Apply|Benefits" } // actions under Teachers
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index",
id = UrlParameter.Optional }
);
}
Any actions you want to be under Teachers should be added to the route. It is actually not necessary to specify defaults for action or page for this route to work (unless you do have some need for a page route value to be captured). The catch here is that a user can still target the actions under Teachers by entering the URL School/{action} because it is caught by the default route. Now this may or may not be a concern for you. Personally I would not consider it such a big issue since the users should just be using the site's navigation instead of entering the URLs manually.

Routing GET and POST routes in ASP.NET MVC 4

I am trying to setup a Login form in an ASP.NET MVC 4 app. Currently, I have configured my view as shown here:
RouteConfig.cs
routes.MapRoute(
"DesktopLogin",
"{controller}/account/login",
new { controller = "My", action = "Login" }
);
MyController.cs
public ActionResult Login()
{
return View("~/Views/Account/Login.cshtml");
}
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model)
{
return View("~/Views/Account/Login.cshtml");
}
When I attempt to visit /account/login in the browser, I receive an error that says:
The current request for action 'Login' on controller type 'MyController' is ambiguous between the following action methods:
System.Web.Mvc.ActionResult Login() on type MyApp.Web.Controllers.MyController
System.Web.Mvc.ActionResult Login(MyApp.Web.Models.LoginModel) on type MyApp.Web.Controllers.MyController
How do I setup a basic form in ASP.NET MVC 4? I've looked at the sample Internet App template in ASP.NET MVC 4. However, I can't seem to figure out how the routing is wired up. Thank you so much for your help.
I haven't tried this yet but can you try annotating your Login actions with the appropriate Http Verb - I'm assuming that you're using a GET for viewing the login page and a POST for processing the login.
By adding [HttpGet] for the first action and [HttpPost] for the second action the theory is that ASP.Net's routing will then know which Action method to call based upon which method has been used. Your code should then look something like this:
[HttpGet] // for viewing the login page
[ViewSettings(Minify = true)]
public ActionResult Login()
{
return View("~/Views/Account/Login.cshtml");
}
[HttpPost] // For processing the login
[ViewSettings(Minify = true)]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model)
{
return View("~/Views/Account/Login.cshtml");
}
If this doesn't work, consider having two routes and two differently named actions like below:
routes.MapRoute(
"DesktopLogin",
"{controller}/account/login",
new { controller = "My", action = "Login" }
);
routes.MapRoute(
"DesktopLogin",
"{controller}/account/login/do",
new { controller = "My", action = "ProcessLogin" }
);
There are other similar questions and answers on StackOverflow already, take a look at: How to route GET and DELETE for the same url and there is also the ASP.Net documentation which might also help.