MVC 4 Anti-Forgery Error with Login - asp.net-mvc

I have an MVC 4 web application with ELMAH running in the background to help me keep track of any errors occurring on the website. I have noticed a good few errors happening stating the following
System.Web.Mvc.HttpAntiForgeryException: The required anti-forgery
form field "__RequestVerificationToken" is not present.
The majority of these errors seem to be happening when a user attempts to log into my website. To be honest, I just used the out of the box MVC 4 Visual Studio login View for this, ie, my View has the following
#using (Html.BeginForm(new { ReturnUrl = ViewBag.ReturnUrl }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
//textboxes for username and password
//login button
}
And then my Account Controller with Login Action
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model, string returnUrl)
{
if (ModelState.IsValid && _accountService.Logon(model.Email, model.Password,false))
{
}
}
Do you think anything looks wrong with this code? I am not sure how to get rid of these errors.
Any feedback or advice would be appreciated.
Thanks.

I've had a user report the same problem and we've identified that it happens when they click the login button twice.
I assume the problem is that logging in changes the token, but when the form is resubmitted it has the old token. One solution would be to disable to login button so the user can only submit the form once.
That's not perfect though, as the login may time out or something and they won't be able to try again without reloading the page, so I'm currently looking for better solutions.

Related

MVC Logoff browser history

I´ve a .net 4.5 app with MVC 4 + WebAPI, and I'm facing a situation that I don´t know how to explain/solve.
My Logoff code is as follows:
public ActionResult SignOut()
{
FormsAuthentication.SignOut();
return Redirect("~");
}
This works, somehow, as expected (didn't verified for hacking scenarios).
However, if I do the following:
public ActionResult SignOut() {
{
FormsAuthentication.SignOut();
return Redirect("~/?logout=true");
}
It seems to still work, however, if the user hits the back navigation button on chrome (or backspace), he gets back to the login page!
Why is that happening?
Is there any other way to pass a parameter to the Redirect call?
Can you confirm that the page you are seeing is not the cache version? Press Shift-F5 and see if the page refreshes or if you are redirected to the login page instead. If that is the case, you can play with the cache settings to make sure users cannot go back to the page.

My ASP.NET MVC app takes me back to the login page when I access the home controller even though I'm logged in

When I log in as a user, and type the url http://localhost:50562/Home/Index into the address bar, the browser takes me to the login page, which is my home page, but the funny thing is that the message "welcome, user!" and Logout link are still there in the upper right corner. When I click on the other links in the nav bar, the session continues normally, meaning, I was never logged out.
Can someone tell me how to configure the routing engine to take me to another page when I access the Home controller while being logged in?
You can achieve this by modifying the POST method for Login in the Account Controller (assuming you are using MVC 4 and the Simple Membership it uses).
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model, string returnUrl)
{
if (ModelState.IsValid && WebSecurity.Login(model.UserName, model.Password, persistCookie: model.RememberMe))
{
return RedirectToLocal(returnUrl);
}
...
}
You can set the routing in a few ways.
return RedirectToAction("Profile", "Home"); ... where Profile is a Controller
return View(returnUrl); ... where you have set and assigned the variable in your method
or retain as above with RedirectToLocal(returnUrl);
Good luck.
What you're describing happens because the user cookie isn't read until after the redirect to the home page is initiated. The best way to initialize your membership provider when users may enter on different pages is to use a global filter.
You can add this functionality for the SimpleMembership provider in the RegisterGlobalFilters method in the FilterConfig class in the App_Start folder
filters.Add(new YourAppNameSpace.Filters.InitializeSimpleMembershipAttribute());
Using a filter keeps you from having to repeat yourself with decorators all over your controller classes. It's easy to overlook adding decorators and, when they aren't there, your site will have unexpected bugs.
Thank you for your answers, but it seems that the project had a SessionObject class all along, which was handled by the controller. I am not the original author of the project, so it took me a while to figure it out. I was tasked to give it some additional features and bugfixes.
I ran into the method:
#region getSessionObject()
protected SessionObject getSessionObject() {
SessionObject loReturnValue = null;
if (Session[SessionObject.SESSION_CONSTANT] != null)
loReturnValue = (SessionObject) Session[SessionObject.SESSION_CONSTANT];
return loReturnValue;
}
It was all I needed to call in the index action of my home controller to check whether the user is logged in or not.

MVC 4 Authorization and strange redirection behavior

I'm in the final stage of my MVC 4 project , adding the authorization to the controller, start causing the redirection to home page each time the user made the submit or request and either he was logged in or out , any idea can help here?
[Authorize]
[HttpPost]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(uoffer uoffer, IEnumerable<HttpPostedFileBase> fileupload)
{
}
delete one of the duplicate attributes - [HttpPost] = [AcceptVerbs(HttpVerbs.Post)]. Then are you using [Authorize] on the Get? You need to have the user authenticated before you try to POST.
Okay finally I figure it thank for fiddle tool, for any one having same behavior, please Notice that the log-off method is made through form submitting.
In my case i was having another block of code doing form.submit() method, my mistake was that I didn't scope and make more specific .
I mean instead of $('form').submit()... it should be $('form#formID').submit() >
Since the first one was general scope and when I was trying to firing it, it was triggering The Log Off form instead of My form.

Best way to implement form in Layout page in ASP.Net MVC 3 project

My site has a login box in the Layout page on which every page is based. When the Login button is clicked, no matter it is successful or failure, the user will be sent to the same page where the Login button is clicked.
The login form is posted to Account.Login action method. However, how can I return the correct view at the end of this action method? I also want to show error information beside the login box ie: wrong username/password in the case of failure to login.
What's the best way to design and implement this?
[HttpPost]
public ActionResult Login(LoginModel model)
{
if(ModelState.IsValid)
{
//TODO : Log user in
return View("ValidModelView");
}
// If login model is not valid
return View("InvalidModelView");
}
One approach might be this. However, I am not sure whether it is the best one.
Just do a Redirect on successful login.
return RedirectToAction("Index", "Home");

ASP.NET MVC - How to maintain ModelState from a different controller?

I have a HomeController with an Index action that shows the Index.aspx view. It has a username/password login section. When the user clicks the submit button, it POSTs to a Login action in the AccountController.
<% Html.BeginForm("Login", "Account", FormMethod.Post); %>
In that action, it tests for Username/Password validity and if invalid, sends the user back to the Login page with a message that the credentials were bad.
[HttpPost]
public ActionResult Login(LoginViewModel Model, string ReturnUrl)
{
User user = MembershipService.ValidateUser(Model.UserName, Model.Password);
if (user != null)
{
//Detail removed here
FormsService.SignIn(user.ToString(), Model.RememberMe);
return Redirect(ReturnUrl);
}
else
{
ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
// If we got this far, something failed, redisplay form
return RedirectToAction("Index", "Home"); // <-- Here is the problem. ModelState is lost.
}
But here's the problem: the ValidationSummary is always blank because we're losing the Model when we RedirectToAction.
So the question is: How do I send the user to the action on a different controller without a Redirect?
As others have said it's common to return the view if validation fails but as you are calling from your account controller you will want to specify the full path of your view
return View("~/Views/Home/Index.aspx", model);
Or
It is also common to have a seperate login page and redirect to that page if the login fails. Both pages will submit to the same login action. Facebook does this for example.
Or
As you only want to display an error message
return RedirectToAction("Index", "Home", new { LoginAttempts = 1 });
then in your Index action read the LoginAttempts parameter and choose to display the error message accordingly.
Use TempData to save state between requests. Use special attributes for convenience as shown here.
Few moments to mention:
Don't return View directly from your POST-action, respect Post-Redirect-Get pattern.
Don't overuse TempData. It's only supposed to save model state right before redirect and to retrieve it right after being redirected.
Well you could always do this
return View("~/Views/Home/Index.aspx", myModel);
It's not a real redirect, the clients url will still point to /login/ but at least you have your modalstate
Three options
You could call the action directly, but the client side will not have its URL changed. So instead of calling RedirectToAction you could call the Index() method of the HomeController class directly.
HomeController c = new HomeController();
c.ViewData = this.ViewData;
return c.Index(data);
The one is a bit tricky. Maybe you will have to set other things as well apart from ViewData which is needed for ModelState.
You could as well use TempData dictionary and fill it with whatever data you want and use that.
The simplest one where you provide full path to the view
return View("~/Views/Home/Index.aspx", data);
A better suggestion used by big players
If we look at how other sites do this kind of scenario. Take for instance Twitter (As #David says Facebook apparently does it the same). You can sign in from the Home/Index action (so to speak if it was developed using Asp.net MVC). But when login fails it displays a separate login page, that displays validation errors. In your case it would be Account/SignIn. Which would make sense and you could directly return its view with validation errors. When everything would be ok, you'd do it as you do it now. Redirect back to Home/Index.
Try using
return View("Index", "Home", Model)

Resources