I am using ASP.NET Core 2.2 with AspNetCore.Authentication.
I am trying to use Post
call in my cshtml
_Layout's view component's form:
<form asp-controller="Home" asp-action="SearchResults" method="post" enctype="multipart/form-data" asp-antiforgery="true" novalidate>
(...)
</form>
My intetntion is to mask URL with search properties to make web crawling and scraping more difficult. My Post
method is updating static fields of my controller, and redirecting to my Index Get
method, where are displayed search results:
private static SearchViewModel _searchModel;
private static string _sortOrder;
private static int? _pageNumber;
(...)
[HttpPost]
[AllowAnonymous]
public IActionResult SearchResults(SearchViewModel searchModel, string sortOrder, int? pageNumber = 1)
{
_searchModel = searchModel;
_sortOrder = sortOrder;
_pageNumber = pageNumber;
return RedirectToAction(nameof(Index));
}
And Get
method signature:
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> Index()
{
//refering to updated fields
}
Idea inspired with this SO reply.
And Setup.cs identity/authentication setup:
public void ConfigureServices(IServiceCollection services)
{
services.AddIdentity<AppUser, IdentityRole>(opts => {
opts.User.RequireUniqueEmail = true;
opts.User.AllowedUserNameCharacters = null; //disable validation
opts.Password.RequiredLength = 8;
opts.Password.RequireNonAlphanumeric = false;
opts.Password.RequireLowercase = false;
opts.Password.RequireUppercase = false;
opts.Password.RequireDigit = true;
})
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddAntiforgery(x => x.HeaderName = "X-CSRF-TOKEN");
.Services.ConfigureApplicationCookie(options =>
{
//previous cookies not valid
options.SlidingExpiration = true;
options.ExpireTimeSpan = TimeSpan.FromDays(1);
});
services.AddMvc(options =>
{
options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
options.Filters.Add<AuditUserActionFilter>();
});
(...)
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IAntiforgery antiforgery)
{
(...)
app.Use(next => context =>
{
if (context.Request.Path == "/")
{
var tokens = antiforgery.GetAndStoreTokens(context);
context.Response.Cookies.Append("CSRF-TOKEN", tokens.RequestToken, new CookieOptions { HttpOnly = false });
}
return next(context);
});
app.UseAuthentication();
}
}
I belive that this is a key: Actually, when I am submitting Post
form as a guest (not authenticated), everything works fine. But after signing in, on form submit I am receiving an exception:
Provided antiforgery token meant for a different claims-based user
Also please find below my user authentication process:
if (user != null)
{
await _signInManager.SignOutAsync();
var result = await _signInManager.PasswordSignInAsync(user, details.Password, false, false);
if (result.Succeeded)
{
if (!string.IsNullOrEmpty(returnUrl) && Url.IsLocalUrl(returnUrl))
return Redirect(returnUrl);
else
return RedirectToAction(nameof(Account));
}
else if (result.IsLockedOut || result.IsNotAllowed)
{
ViewBag.Message = "You are not allowed to sign in.";
return View("Error");
}
else
{
ModelState.AddModelError(nameof(UserLoginViewModel.Password), "Incorrect password.");
return View("Index");
}
}
I was trying several approaches from here, and here with no results. But they are for older versions of ASP.NET Core. I am not sure how I am supposed to update my antiforgery tokens for authenticated user?
Possible reasons of my problem:
- misunderstanding of Post -> Get form call.
- not understanding how authentication cookies works in ASP.NET Core authentication.
- misunderstanding of authentication process in general.
P.S. When I remove
options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
from:
services.AddMvc(options =>
{
options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
options.Filters.Add<AuditUserActionFilter>();
});
I am able to make Post
call. But I belive, that it is not any surprise.
EDIT: Because of some reason, when I submit form from another view (form is in _Layout's view component) than Home/Index (where I display search results), there is no antiforgery exception.
Is it because HomeController
's Index
action is Get
? After adding Post / Get
attribute:
[AllowAnonymous]
[HttpGet, HttpPost]
public async Task<IActionResult> Index()
I have same exception as before. After adding:
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Index()
I am not able to load the view at all, because it is asking for the token.
How to bypass it?