Home > database >  reCAPTCHA v3 token generation after submitting form in ASP.NET Core using Javascript
reCAPTCHA v3 token generation after submitting form in ASP.NET Core using Javascript

Time:08-19

I am trying to specifically implement the action of only setting the token value once the form is submitted, instead of setting the value on page load and resetting the token every 90 seconds or so before the token expiration. These two stackoverflow questions don't help in that regard, see here and here.
for example, here is my form:

<form id="contact" method="post">
    <input type="hidden" asp-for="Token" />
    <p><label asp-for="Name">Name</label> <input type="text" asp-for="Name" required /></p>
    <p><label asp-for="Company">Company</label> <input type="text" asp-for="Company" /></p>
    <p><label asp-for="EmailAddress">Email</label> <input type="email" asp-for="EmailAddress" required /></p>
    <p><label asp-for="PhoneNumber">Phone</label> <input type="tel" asp-for="PhoneNumber" required /></p>
    <p><label asp-for="Message">Message</label> <textarea asp-for="Message" required></textarea></p>
    <p><input type="submit" value="Send" id="submit"/></p>
</form>

I have tried to preventDefault on the form submission then call either submit or requestSubmit on the form after the token is loaded and both instances will lead to an Antiforgery token validation failed issue as described here. For my JS part, I am waiting for ready to load the library, then I will use that to bind listeners to the form submit event, then execute when form is submitted:

grecaptcha.ready(function() {
    document.getElementById("contact").addEventListener("submit", function(e) {
        e.preventDefault();
        grecaptcha.execute("@Model.reCaptchaSiteKey", {action: "contact"}).then(function(token) {
            document.getElementById("Token").value = token;
            document.getElementById("contact").requestSubmit((document.getElementById("submit"));
        });
    });
});

I have also tried to disable the antiforgery request verification using the form attribute asp-antiforgery="false" but through more reading figured this shouldnt be done as there is no good reason too.
How can I achieve this
EDIT
even setting the form attribute asp-antiforgery="false", I still get the following error.

Antiforgery token validation failed. The required antiforgery request token was not provided in either form field "__RequestVerificationToken" or header value "RequestVerificationToken".

UPDATE
To clarify, I can get this to work no problem by generating a token upon page load and refreshing to token every 90 seconds as shown in the code below. But, I do not want to go this route. Is there not a way to only add the value of the token after submission?

grecaptcha.ready(function() {
    grecaptcha.execute("@Model.reCaptchaSiteKey", {action: "submit"}).then(function(token) {
        document.getElementById("Token").value = token;
    });
});
setInterval(() => {
    grecaptcha.ready(function() {
        grecaptcha.execute("@Model.reCaptchaSiteKey", {action: "submit"}).then(function(token) {
            document.getElementById("Token").value = token;
        });
    });
}, 90 * 1000);  

CodePudding user response:

Since you are simply using a generic form (Snippet from your example below)

<form id="contact" method="post">

The ASP.NET Core Automatic Tag Helper addition of the AntiForgery token isn't happening. (You can validate this theory by viewing source and looking for the AntiForgery token)

If you don't utilize the tag helper using asp- style elements on the form tag you will need to add a validation token manually using something like this

@Html.AntiForgeryToken()

CodePudding user response:

To close this issue
I have got this working, the issue was that I was preventing the default action of the form when pressing the input button to submit it. When doing so, the data in the form was still sent to the backend and thus, it didnt matter if I added the token value after since the backend was already populated with the Token being blank.
To fix this, I prevented the default action of the input button on click, then added the token value, and then resubmitted the form with the input as the submitter as shown below:

<script>
    grecaptcha.ready(function() {
        let form = document.getElementById("contact");
        let submitBtn = document.getElementById("sub-btn");
        submitBtn.addEventListener("click", function(event) {
            event.preventDefault();
            grecaptcha.execute("@Model.reCaptchaSiteKey", {action: "contact"}).then(function(token) {
                document.getElementById("Token").value = token;
                form.requestSubmit(submitBtn);
            });
        });
    });
</script>

With the input button on the form as follows:

<p><input type="submit" value="Send" id="sub-btn" /></p>
  • Related