I've been trying to send the AntiForgeryToken
through AJAX request in an ASP.NET Core MVC (area based) Razor view; however, I am definitely missing something to send in the POST
request. I read through several documentations and StackO. answers, but all of them are missing something common that I found on this MS documentation: Preventing Cross-Site Request Forgery (CSRF) Attacks in ASP.NET MVC Application. I looked at this StackO. question and the replies, and the common difference is that the MS documentation sends both the cookie and form (hidden element) token but I get an error message saying AntiForgery.GetTokens(null, out cookieToken, out formToken)
does not exist in the current context.
I also found this and thought could be really helpful but don't know how to implement into the view.
This SO question is the same as my question, only difference being I want to still use the normal (and non-customized) [ValidateAntiForgeryToken]
attribute.
I want to know and learn the right and most secure way to send both the cookie and form antiforgery tokens through ajax post requests while using the [ValidateAntiForgeryToken]
attribute. Additionally, if there are any flaws or security or usage issues in the code, please do correct me! Any help would be greatly appreciated! Thanks!
Here's my code in the MVC view:
@{
ViewData["Title"] = "Kiosk";
}
@model ...
@using (Html.BeginForm("Index", "Kiosk"))
{
@Html.AntiForgeryToken()
}
@using Microsoft.AspNetCore.Antiforgery
<h1>Check-in Kiosk</h1>
<title>@ViewData["Title"]</title>
<h2>Follow the instructions below.</h2>
<p id="instructions" style="color: green;">
Slide your ID card into the card-holder and wait for further instructions.
</p>
<div id="row">
<div class="col-md-4">
<form method="post" onsubmit="getData()">
<div asp-validation-summary="All" class="text-danger pb-2" id="errors"></div>
<div class="form-floating pb-2">
<input asp-for="ScannedCode" class="form-control" id="scancode"/>
<label asp-for="ScannedCode"></label>
<span asp-validation-for="ScannedCode" class="text-danger"></span>
</div>
<div class="form-floating pb-2">
<input asp-for="Number" class="form-control" id="number"/>
<label asp-for="Number"></label>
<span asp-validation-for="Number" class="text-danger"></span>
</div>
<div class="form-group pt-4">
<input type="submit" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<script>
function getFormToken() {
let token = $('input[name="`__RequestVerificationToken`"]').val();
return token;
}
@functions {
public string TokenHeaderValue()
{
string cookieToken, formToken;
AntiForgery.GetTokens(null, out cookieToken, out formToken);
return cookieToken + ":" + formToken;
}
}
function getData() {
let scannedCode = document.getElementById("scancode").innerText;
let number = document.getElementById("number").innerText;
sendData(scannedCode, number);
}
function sendData(ScannedCode, Number) {
let realdata = {
ScannedCode: ScannedCode;
Number: Number;
};
$.ajax(
{
type: "POST",
url: "@Url.Action("Index", "Kiosk")",
data: realdata,
dataType: 'json',
headers: 'RequestVerificationToken': '@TokenHeaderValue()',
success: function (response) {
if (response.success == true) {
document.getElementById("id").hidden;
document.getElementById("instructions").innerText = response.message;
setTimeout(function () {
location.reload();
}, response.seconds);
}
else {
document.getElementById("instructions").innerText = response.message;
setTimeout(function () {
location.reload();
}, response.seconds);
}
}
}
)
}
</script>
Here's the method simply decorated with the [ValidateAntiForgeryToken]
attribute:
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Index([Bind("ScannedCode,Number")] CodeModel input) {
//Implementation not shown
}
You can use below code
let token = $('input[name="__RequestVerificationToken"]').val();
to get token, here is my test result and test code.
Test Result
KioskController.cs
using System.Diagnostics;
using Microsoft.AspNetCore.Mvc;
using MVCWebApplication.Models;
namespace MVCWebApplication.Controllers;
public class KioskController : Controller
{
private readonly ILogger<KioskController> _logger;
public KioskController (ILogger<KioskController > logger)
{
_logger = logger;
}
[HttpGet]
public IActionResult Index()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Index([Bind("ScannedCode,Number")] TestModel input)
{
return Json(new { success = true, message = "Data received successfully", seconds = 3 });
}
}
TestModel.cs
namespace MVCWebApplication.Models
{
public class TestModel
{
public string? ScannedCode { get; set; }
public string? Number { get; set; }
}
}
/Kiosk/Index.cshtml
@{
ViewData["Title"] = "Kiosk";
}
@model MVCWebApplication.Models.TestModel
@using (Html.BeginForm("Index", "Kiosk", FormMethod.Post, new { id = "kioskForm" }))
{
@Html.AntiForgeryToken()
<div class="form-floating pb-2">
<input asp-for="ScannedCode" class="form-control" id="scancode"/>
<label asp-for="ScannedCode"></label>
<span asp-validation-for="ScannedCode" class="text-danger"></span>
</div>
<div class="form-floating pb-2">
<input asp-for="Number" class="form-control" id="number"/>
<label asp-for="Number"></label>
<span asp-validation-for="Number" class="text-danger"></span>
</div>
<div class="form-group pt-4">
<button type="button" class="btn btn-primary" onclick="getData()">Submit</button>
</div>
}
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
function getData() {
let scannedCode = $('#scancode').val();
let number = $('#number').val();
sendData(scannedCode, number);
}
function sendData(scannedCode, number) {
let realdata = {
ScannedCode: scannedCode,
Number: number
};
let token = $('input[name="__RequestVerificationToken"]').val();
$.ajax({
type: "POST",
url: "@Url.Action("Index", "Kiosk")",
data: realdata,
headers: {
"RequestVerificationToken": token
},
success: function (response) {
if (response.success) {
$('#instructions').text(response.message);
setTimeout(function () {
location.reload();
}, response.seconds * 1000);
} else {
$('#instructions').text(response.message);
}
},
error: function (xhr, status, error) {
console.error(xhr.responseText);
}
});
}
</script>
<h1>Check-in Kiosk</h1>
<p id="instructions" style="color: green;">
Slide your ID card into the card-holder and wait for further instructions.
</p>