I have written some simple code to illustrate the problem.
The controller code:
public ActionResult Edit()
{
string un = User.Identity.Name;
return View();
}
[HttpPost]
public ActionResult Edit(int? dummy)
{
string un = User.Identity.Name; // <-- here it's empty string
return View();
}
The Edit.cshtml view code:
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<input type="submit" value="Submit" />
}
The User.Identity.Name is not empty after I log in and I go to the Edit page.
But after I submit the Edit page (I make a HTTP POST) the User.Identity.Name becomes empty string and remains empty string no matter what page I access.
In Web.config I have:
<authentication mode="Forms">
<forms loginUrl="~/Account/LogOn" timeout="2880" />
</authentication>
This sounds like a strange problem, and though I can't give you an answer yet, this might help.
FormsAuthentication works by setting a cookie on the browser called .ASPXAUTH. After you sign in, inspect the cookies in your browser and look for it. Also, you can inspect the Request property in the controller action methods to look for that cookie. It will be a weird-looking encoded value. You can also use something like Fiddler2 to make sure that the cookie is being sent when you post the form.
I have run into this problem before, but it had to do with cross-domain requests and machineKey mismatch problems. If both your Edit actions are in the same controller / project / URL / web.config, then perhaps something in the pipeline is removing the cookie, or reconfiguring your User principal during POST requests..? You aren't using OWIN anywhere in the project, right? And there are no global filters other than HandleErrorAttribute?
Related
As I have deployed my newly created Asp.Net MVC web application to the server, I am facing the subject error, upon submitting a sign-up form. It is working fine in my local environment.
In my controller's action method, I have set the attribute to validate the token as shown below:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
////
}
And in my view, I have set as:
#using (Html.BeginForm("Register", "Account", FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
#Html.AntiForgeryToken()
}
////
)
One more thing is that I have enabled SSL on my project properties. I have also added <httpCookies httpOnlyCookies="true" requireSSL="true"/> in my web.config file.
While inspecting, I can see the hidden element <input name="__RequestVerificationToken" type="hidden" value="blaa blaa>", but i cannot see any cookie present there.
After hours of troubleshooting, I am still unable to find a solution to this problem. I am always getting error The required anti-forgery cookie "__RequestVerificationToken" is not present.
How to get rid of this?
I myself has find the answer to this question. In my case, as I have set SSL to true, I need to have a security certificate. So, I purchased and configured the certificate on my hosted site and the error has gone.
When navigating to http://localhost:62030/Home/About and the Home controller includes the [Authorize] attribute the app correctly returns a 302 redirect to http://localhost:62030/Account/Login but w/o a ReturnUrl instead of http://localhost:62030/Account/Login?ReturnUrl=%2FHome%2FAbout
This seems to have started recently however I'm not aware of the cause. When creating a new mvc project the redirect properly returns a redirect along with the ReturnUrl. Where has the ReturnUrl gone?
You should be sure that you are using ReturnUrl in controller and view
Html.BeginForm("YourLogin", "Account", new {ReturnUrl = Request.QueryString["ReturnUrl"] })
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult YourLogin(LoginInputModel model, string ReturnUrl) {
...
}
Set the form authentication since ASP.NET MVC template is using Forms authentication.
<authentication mode="Forms">
<forms loginUrl="~/Account/Login" timeout="2880" />
</authentication>
You can also refer the link:Return URL
Due to the way the Authorization Filters work ( [Authorize] Works as a Filter )
you would never receive the request in HomeController.
To be using [Authorize] you're probably using Identity as Authentication/Authorization Middleware.
You should define the LoginPath correctly in your web.config.
The required anti-forgery cookie "__RequestVerificationToken" is not present.
I have added in cshtml and in Controller also
using (Html.BeginForm())
{
#Html.AntiForgeryToken()
//some code
}
[HttpGet]
[ValidateAntiForgeryToken]
public ActionResult Index()
{
using (var db = new SampleEntities())
{
return View(db.Rfps.ToList());
}
}
In my case, I had this in my web.config:
<httpCookies requireSSL="true" />
But my project was set to not use SSL. Commenting out that line or setting up the project to always use SSL solved it.
The issue is because you are using a ValidateAntiForgeryToken attribute on a GET request.
You don't need to use this attribute for GET actions. Look here for more information:
In my case, it was because I ran another Asp.Net website before. So the cookies could not match for localhost.
I cleared my cookies (just for localhost) and everything is fine now.
So we have [HttpPost], which is an optional attribute. I understand this restricts the call so it can only be made by an HTTP POST request. My question is why would I want to do this?
Imagine the following:
[HttpGet]
public ActionResult Edit(int id) { ... }
[HttpPost]
public ActionResult Edit(MyEditViewModel myEditViewModel) { ... }
This wouldn't be possible unless the ActionMethodSelectorAttributes HttpGet and HttpPost where used.
This makes it really simple to create an edit view. All the action links just points right back to the controller. If the view model validates false, you just pop right back to the edit view again.
I will be bold and say this is best practice when it comes to CRUDish things in ASP.NET MVC.
EDIT:
#TheLight asked what was needed in the view to accomplish the post. It's simply just a form with method POST.
Using Razor, this would look something like this.
#using (Html.BeginForm())
{
<input type="text" placeholder="Enter email" name="email" />
<input type="submit" value="Sign Up" />
}
This renders the following HTML:
<form action="/MyController/Edit" method="post">
<input type="text" name="email" placeholder="Enter email">
<input type="submit" value="Sign Up">
</form>
When the form is submitted, it will perform an Http Post request to the controller. The action with the HttpPost attribute will handle the request.
Its so you can have multiple Actions that use the same name, you can use the HttpPost attribute to mark which method gets handled on a Post request like so:
public ActionResult ContactUs()
{
return View();
}
[HttpPost]
public ActionResult ContactUs(ContactUsModel model)
{
//do something with model
return View();
}
As far as best practices for HttpGet and HttpPost, it is good practice in any web development to use HttpPost for Creates, Updates, and Deletes (data modification). Post is good because they require a form submission, which prevents users from clicking poisoned links(e.g. [https://www.example.com/Delete/1]) in emails, social sites, etc. and changing data inadvertently. If you are basically just Reading data HttpGet works great.
See OWASP for more in-depth security considerations and why the validation token increases security.
This is mainly so that you can have two Actions with the same name,
one which is used on GETs and perhaps displays a form for user entry and the other being used on POSTs when the user submits the form displayed by the original GET. If the Actions are not differentiated in this way, an error will occur due to being unable to resolve which Action is intended to handle the request.
I have a ASP.NET MVC project, and I would like to have a different LoginUrl for different areas of the website. Depending on the area of the site, different types of credentials are entered.
http://host.com/widget/home should redirect the user to http://host.com/widget/logon.
http://host.com/admin/home should redirect the user to http://host.com/admin/logon.
So far, the best solution I have come up with, is to have Forms Auth loginUrl="~/Account/Logon" in the web.config:
<authentication mode="Forms">
<forms loginUrl="~/Account/LogOn" timeout="2880"/>
</authentication>
In the controller for Account:
public ActionResult LogOn()
{
//redirect depending on the returnUrl?
string returnUrl = ControllerContext.Controller.ValueProvider["ReturnUrl"].AttemptedValue;
if (returnUrl.StartsWith("/widget"))
{
return Redirect(string.Format("/widget/Logon?ReturnUrl={0}", returnUrl));
}
if (returnUrl.StartsWith("/admin"))
{
return Redirect(string.Format("/admin/Logon?ReturnUrl={0}", returnUrl));
}
return View();
}
Is there a better way to do this?
I asked and answered this in my stackoverflow question How to redirect to a dynamic login URL in ASP.NET MVC.
I know that you can have separate web.config files in sub-folders of a website, so that if you had actual .aspx pages within an admin/ folder, and a web.config in that folder, you could specify the authentication url in that folder separately.
I'm not sure if that works with ASP.NET MVC routes as you're probably not going to have physical files in those sub-folders, but it's worth a try.
Add Authenticate attribute to your actions.
Then in global.asax add Application_AuthenticateRequest then look at the sender and redirect there where you want the action to login.