Using MVC 5.1.1/VS 2013 Professional/.Net 4.5
I keep getting error once in a while (from localhost and from production IIS 7):
System.Web.Mvc.HttpAntiForgeryException: The anti-forgery cookie token and form field token do not match.
The issue seems to be when i logout a user, sometimes when i go to authenticate again thats when i get the error.
My authentication code looks like something like this:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginViewModel model)
{
if (ModelState.IsValid)
{
var user = _uow.UserRepository.FindLogin(model.Email, model.Password);
if (user != null)
{
var claims = new List<Claim>();
claims.Add(new Claim(ClaimTypes.Email, user.Email));
claims.Add(new Claim(ClaimTypes.NameIdentifier, user.UserID.ToString()));
claims.Add(new Claim(ClaimTypes.Role, user.UserLevel.ToString()));
var id = new ClaimsIdentity(claims, DefaultAuthenticationTypes.ApplicationCookie);
var ctx = Request.GetOwinContext();
var authenticationManager = ctx.Authentication;
authenticationManager.SignIn(new AuthenticationProperties { IsPersistent = true }, id);
}
else
{
ModelState.AddModelError("", "Invalid Email Address or Password.");
}
}
return View(model);
}
Update with LogOut Method:
[HttpGet]
[AllowAnonymous]
public ActionResult LogOut(LoginViewModel model)
{
Session.Abandon();
return SignOffUser();
}
private ActionResult SignOffUser()
{
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
return RedirectToAction("Index", "Home");
}
LogOut Form View
#if (Request.IsAuthenticated)
{
using (Html.BeginForm(null, null, FormMethod.Post, new {id = "logoutForm", #class = "navbar-right"}))
{
#Html.AntiForgeryToken()
....
}
}
Another thing you may want to look at is that on your logout page, you don't necessary validate the forgery token.
Try changing this:
[HttpGet]
[AllowAnonymous]
public ActionResult LogOut(LoginViewModel model)
{
To this
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult LogOut(LoginViewModel model)
{
My bet is that the token is changing on the page, and since it's not validated, ASP.NET doesn't know if it is truly correct or not.
E: just noticed, it should actually be an httppost on logout
Show your logout form(view).
This happens if you are calling the logout method in your controller from your view but don't have antiforgerytoken generated inside the form.
Your form should look like
#using(Html.BeginForm("Action","Controller"))
{
#Html.antiforgerytoken()
....
}
Then the action you call via your view should look like
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult MyAction()
{
return content("hello user! Antiforgerytoken has been validated1");
}
PS: No matter what action method your form calls, if you want to have antiforgery token that needs to be validated, then the form and action method should look like what I've mentioned above.
Try setting explicitly machine key in web.config (under system.web):
<machineKey validationKey="971E32D270A381E2B5954ECB4762CE401D0DF1608CAC303D527FA3DB5D70FA77667B8CF3153CE1F17C3FAF7839733A77E44000B3D8229E6E58D0C954AC2E796B" decryptionKey="1D5375942DA2B2C949798F272D3026421DDBD231757CA12C794E68E9F8CECA71" validation="SHA1" decryption="AES" />
Here's a site that generate unique Machnie Keys http://www.developerfusion.com/tools/generatemachinekey/
link: The anti-forgery cookie token and form field token do not match in MVC 4
Related
I write a new ASP.NET MVC 5 application and I have some problems with the authentication. I want to have two registration and login forms (for users and for companies). I use the basic table ApplicationUser for Users and make my own table CompaniesAccountModel for companies. But the problem comes when I use the UserManager and SignInManager. I can't modify them to work with CompaniesAccountModel. Here you are some code.
[AllowAnonymous]
public ActionResult CompanyRegister()
{
return View();
}
//
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult CompanyRegister([Bind(Include = "CompanyName, Password, Email, ConfirmPassword")] CompanyAccountModel model)
{
if (ModelState.IsValid)
{
db.CompanyAccountModels.Add(model);
db.SaveChanges();
return RedirectToAction("Index", "Home");
}
// If we got this far, something failed, redisplay form
return View(model);
}
and
[AllowAnonymous]
public ActionResult CompanyLogin(string returnUrl)
{
ViewBag.ReturnUrl = returnUrl;
return View();
}
//
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> CompanyLogin(CompanyLoginViewModel 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.CompanyName, model.Password, model.RememberMe, shouldLockout: false);
switch (result)
{
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Invalid login attempt.");
return View(model);
}
}
I want to use the UserManager and SignInManager for the companies registration and login. If someone have an idea how to do this, it would be well.
You could easily customize authentication process for your company users. And use it side by side with existing method for ordinary users. Consider this example as a clue:
public ActionResoult CompanyLogin(CompanyLoginViewModel model, string returnUrl)
{
// imaging you have own company manager, completely independent from identity
// you could check validity of company by own preferred logic
if(_companyManager.IsValid(model))
{
// company is valid, going to authenticate
var ident = new ClaimsIdentity(
new[]
{
// adding following 2 claim just for supporting default antiforgery provider
new Claim(ClaimTypes.NameIdentifier, model.CompanyName),
new Claim("http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider", "ASP.NET Identity", "http://www.w3.org/2001/XMLSchema#string"),
// an optional claim you could omit this
new Claim(ClaimTypes.Name, model.CompanyName),
// add this role to differentiate from ordinary users
new Claim(ClaimTypes.Role, "Company"),
// you could even add some role
new Claim(ClaimTypes.Role, "AnotherRole"),
// and so on
},
DefaultAuthenticationTypes.ApplicationCookie);
// Identity is sign in user based on claim don't matter
// how you generated it Identity
HttpContext.GetOwinContext().Authentication.SignIn(
new AuthenticationProperties { IsPersistent = false }, ident);
// auth is succeed,
return RedirectToAction("MyAction");
}
ModelState.AddModelError("", "We could not authorize you :(");
return View();
}
Since we injected our logic to Identity, we don't need to do extra thing at all.
[Authorize]
public ActionResult MySecretAction()
{
// all authorized users could use this method don't matter how has been authenticated
// we have access current user principal by calling also
// HttpContext.User
}
[Authorize(Roles="Company")]
public ActionResult MySecretAction()
{
// just companies have accesses to this method
}
Also if both ApplicationUser and Company classes share lots in common you could just extend Company from ApplicationUser. By doing so you don't need to write extra login method. Same login works for both. But if for any reason you don't want inherit Company from ApplicationUser my above solution more desirable.
I have the following:
[Route("whitelist")]
[Authorize(Roles = "Administrator")]
public ActionResult Whitelist() {
var vm = new WhitelistViewModel();
return View(vm);
}
[Route("login")]
[AllowAnonymous]
public ActionResult Login(string returnUrl) {
ViewBag.ReturnUrl = returnUrl;
return View();
}
However, when I navigate to /whitelist as an unauthenticated user, I get navigated to /Account/Login, which is invalid. How do I tell MVC5 to use the attribute routes when redirecting in this case?
In App_Start/Startup.Auth.cs, you'll need to change your default login path for redirects.
LoginPath = new PathString("/login")
The reason is since you have added an attribute route, then the existing route is invalid.
I copy from some website, I using notepad to combine, but ideally is here.
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
IPrincipal user = filterContext.HttpContext.User;
if (!user.Identity.IsAuthenticated)
{
// Redirect to login page
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary
{
{ "controller", "Login" },
{ "action", "NoAccess" }
});
}
}
I am using asp.net mvc 5 and using custom form authetication. When I try to login it login perfectly but when I try to logoff it redirect me to logiurl which I have set in webconfig file which is give below.
<authentication mode="Forms">
<forms loginUrl="~/Account/Login" timeout="30" />
</authentication>
Rather than take me to the page for which redirection is defined in Logoff action method as shown below.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Logoff()
{
_formsAuth.SignOut();
return RedirectToAction("Index", "Home");
}
Everything is working fine in chrome browser but it is not working in firefox.
this method as been deprecated, tho it is useful for testing, i'm not using it anymore and rather use the already implement template which I customize to my own, when it comes to security, customers comes first.
that being said, I'm left with unanswered question about classes and models that you use.
Using the forms authentication feature requires calls to two static methods of the System.Web.Security.FormsAuthentication class:
The Authenticate method validates credentials supplied by the user.
The SetAuthCookie method adds a cookie to the response to the
browser, so that users do not need to authenticate every time they
make a request.
public class FormsAuthProvider : IAuthProvider {
public bool Authenticate(string username, string password){
bool result = FormsAuthentication.Authenticate(username, password);
if (result) {
FormsAuthentication.SetAuthCookie(username, false);
}
return result;
}
public class AccountController : Controller {
IAuthProvider authProvider;
public AccountController(IAuthProvider auth) {
authProvider = auth;
}
public ViewResult Login() {
return View();
}
[HttpPost]
public ActionResult Login(LoginViewModel model, string returnUrl) {
if (ModelState.IsValid) {
if (authProvider.Authenticate(model.UserName, model.Password)) {
return Redirect(returnUrl ?? Url.Action("Index", "Admin"));
} else {
ModelState.AddModelError("", "Incorrect username or password");
return View();
}
} else {
return View();
}
}
}
In VisualStudio 2013 RC, using MVC5, in AccountController.cs, I had to modify the Login method to call WebSecurity.Login as I'm still using WebSecurity and Webmatrix to add users and user roles. I populate users and roles with WebSecurity and Webmatrix API using an Entity Framework 6 seed method. In my code examples WebSecurity.Login will handle a local user account, and CheckPasswordAndSignInAsync will handle a Google account login.
Should OWIN be a replacement for Webmatrix and the Websecurity API, if so what is Microsoft's OWIN API to create roles and users? Can't seem to find much documentation on OWIN in MSDN. Has anyone created any sample code or aware of any docs that explain
how to use Microsofts' flavor of OWIN? Do you have to write your own code to populate
roles and users to the OWIN generated tables such as AspNetUsers and AspNetUserRoles?
This is login method:
public async Task Login(LoginViewModel model, string returnUrl)
{
if (ModelState.IsValid)
{
// Validate the password
IdentityResult result = await IdentityManager.Authentication.CheckPasswordAndSignInAsync(AuthenticationManager, model.UserName, model.Password, model.RememberMe);
if (result.Success)
{
return RedirectToLocal(returnUrl);
}
//added this for roles to work
else if (ModelState.IsValid && WebSecurity.Login(model.UserName, model.Password, persistCookie: model.RememberMe))
{
return RedirectToLocal(returnUrl);
}
else
{
AddErrors(result);
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
This is signout method:
// POST: /Account/LogOff
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LogOff()
{
AuthenticationManager.SignOut();
WebSecurity.Logout();
return RedirectToAction("Index", "Home");
}
In order to create a user, you need to write code like this:
UserManager<ApplicationUser> userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));
var user = new ApplicationUser() { UserName = model.UserName };
// Add the Admin role to the user.
user.Roles.Add(new IdentityUserRole() { Role = new IdentityRole("Admin") });
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
// Code after successfully creating the user.
}
using VS'12 asp.net MVC4 C# InternetApplication with KendoUI and Simplemembership EF Code first.
this is what VS gives you to start out with.
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Register(RegisterModel model)
{
if (ModelState.IsValid)
{
// Attempt to register the user
try
{
WebSecurity.CreateUserAndAccount(model.UserName, model.Password);
WebSecurity.Login(model.UserName, model.Password);
return RedirectToAction("Index", "Home");
.
.
.
Here is where i would like it to RedirectToAction to my Controller ChangeUsersInfoController, and send it the id of the newly created user.
To get the ID i have tried
1.
var UserID = (from u in db.UserProfiles
where u.UserName == User.Identity.Name
select new
{
UserID = u.UserID
}).Single();
2.
var userID = System.Web.Security.Membership.GetUser().ProviderUserKey;
return RedirectToAction("Edit","ChangeUsersInfo", userID);
I have also Tried return RedirectToAction("Edit","ChangeUsersInfo"); to see if it would let me jsut send it there without a variable.
For some reason
variable returned = Null
variable returned = Null
doesnt work because 1 and 2 didnt
seems to not redirect to the new EDIT but to the LOG IN Screen
What is the reason behind my nulls and the fail Redirect?
Yes the database has users
var userID = System.Web.Security.Membership.GetUser().ProviderUserKey;
Using the WebSecurity and System.Web.Security items will only work on subsequent request because you have not sent the auth cookie back to the client yet (this is where it pulls its data from). Given that, you should not even have to send the user Id of the user in the redirect URL...they are logged in and you can just get it at the controller/action that you are going to. By grabbing the userId from the auth ticket, you now do not have to check if the user Id passed in the URL is actually the user that is currently logged in.
Example:
Login Method
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Register(RegisterModel model)
{
if (!ModelState.IsValid)
{
//send em back with the errors
Return View();
}
try
{
WebSecurity.CreateUserAndAccount(model.UserName, model.Password);
WebSecurity.Login(model.UserName, model.Password);
return RedirectToAction("Index", "ChangeUsersInfo");
}...
}
Change User Info Controller
[Authorize]
public ActionResult Index()
{
var userId = WebSecurity.CurrentUserId;
var model = GetUserInfoService.GetUserInfo(userId);
Return View(model);
}