admin管理员组文章数量:1327661
I'm in a bind, and Javascript world it's very weird.
I need to make a form with the reCAPTCHA v3 token resolved before proceeding with form submission. Since some events are bound to the form submit
event, the event loop must wait until it's resolved otherwise it fails:
- Some forms have validation events that prevent the submission if there are errors (like an invalid value inside an input).
- If the user clicks the form too quickly (thanks to the autoplete) when the page loads, and the token is still not retrieved, the request returns an error.
While I'm trying to do is to have a listener for the submit
event that blocks the submission until the token is resolved before going on with other listeners.
Currently the documentation on the reCAPTCHA script is zero, and I haven't found a reliable way to actually and forcefully resolve the token. The reCAPTCHA script is asynchronous, so there is no way to wait until the token is resolved:
// Let's retrieve the form by its id.
let form = document.getElementById('example_form');
let addToken = (form) => {
grecaptcha.execute('site_key', {
// some options
}).then(token => {
// Include the token as an input inside the form so it's sent.
});
};
form.addEventListener('submit', () => {
return addToken(form); // <-- This won't work, since it's async.
});
I'm in a bind, and Javascript world it's very weird.
I need to make a form with the reCAPTCHA v3 token resolved before proceeding with form submission. Since some events are bound to the form submit
event, the event loop must wait until it's resolved otherwise it fails:
- Some forms have validation events that prevent the submission if there are errors (like an invalid value inside an input).
- If the user clicks the form too quickly (thanks to the autoplete) when the page loads, and the token is still not retrieved, the request returns an error.
While I'm trying to do is to have a listener for the submit
event that blocks the submission until the token is resolved before going on with other listeners.
Currently the documentation on the reCAPTCHA script is zero, and I haven't found a reliable way to actually and forcefully resolve the token. The reCAPTCHA script is asynchronous, so there is no way to wait until the token is resolved:
// Let's retrieve the form by its id.
let form = document.getElementById('example_form');
let addToken = (form) => {
grecaptcha.execute('site_key', {
// some options
}).then(token => {
// Include the token as an input inside the form so it's sent.
});
};
form.addEventListener('submit', () => {
return addToken(form); // <-- This won't work, since it's async.
});
Share
Improve this question
edited May 26, 2020 at 7:53
Brian Tompsett - 汤莱恩
5,89372 gold badges61 silver badges133 bronze badges
asked May 26, 2020 at 6:17
DarkGhostHunterDarkGhostHunter
1,5503 gold badges13 silver badges25 bronze badges
4 Answers
Reset to default 5I had the same issue. This is my solution:
async function recaptchaCall(){
var recaptcha_token = '';
grecaptcha.ready(() => {
grecaptcha.execute(grecaptchaKey, {action: 'submit'}).then((token) => {
recaptcha_token = token;
});
});
while(recaptcha_token == ''){
await new Promise(r => setTimeout(r, 100));
}
return recaptcha_token;
}
let recaptcha_token = await recaptchaCall();
I think it's the best solution since it is not possible to make it as an synchronously function correctly. I'm using setTimeout to wait until I receive Recaptcha answer. I hope it is helpful.
Figured out, there is no way to make recaptcha.execute()
synchronous. In other words, waiting for the token to be resolved from servers is impossible.
Instead, you should hammer nonchalantly the reCAPTCHA servers by requesting an initial token, and then setting up the same action through an interval of 100 seconds to be sure the token is received before expiration.
This code is adjusted for many forms. Use at your own risk.
const site_key = 'HEREYOURSITEKEY';
// This function retrieves the token from reCAPTCHA.
const retrieveToken = (form) => {
// Mark the form as unresolved until the retrieval ends.
form.unresolved = true;
// Get the token.
grecaptcha.execute(site_key, {
action: form.action.substring(action.indexOf('?'), action.length).replace(/[^A-z\/_]/gi, '')
}).then(token => {
// Append the token to the form so it's sent along the other data.
let child = document.createElement('input');
child.setAttribute('type', 'hidden');
child.setAttribute('name', '_recaptcha');
child.setAttribute('value', token);
form.appendChild(child);
form.unresolved = false;
});
};
// We will loop for each form in the document. You can add a "filter" method if you want.
Array.from(document.getElementByTagName('form'))
.forEach(form => {
// Mark it as unresolved from the beginning.
form.unresolved = true;
// Add an event listener that disables submission if the form is unresolved.
form.addEventListener('submit', event => {
if (form.unresolved) {
event.preventDefault();
}
});
// Resolve the token at startup.
retrieveToken(form);
// And retrieve a new token each 100 seconds. Bite me.
setInterval(() => refreshToken(form), 100 * 1000);
});
You could just disable form submission until the token is loaded and show some loader instead. In the then
callback, you save the token in a form field and enable the submission of the form (and hide the loader).
If you want to make it more seamless, you could hide this process from the user: If the user submits the form before the token is loaded, then you disable the button (and prevent the regular submission attempt) and show a loader instead and remember that a submission was pending, and when the token is later received you check if a submission was pending, and if yes, you programmatically submit the form (including the token).
This way the user won't encounter the loader unless they actually submit the form too early.
JavaScript cannot wait for the recaptcha request to finish. I made that work with a timestamp global variable that I pare to the current timestamp at each submit. So, onSubmit has 2 behaviours: if requesting a new token is necessary, then onSubmit only takes care of the recaptcha token (submission is prevented). If not necessary, then it does the usual (validations and stuff).
<script src="https://www.google./recaptcha/api.js?render=SITE_KEY"></script>
<script>
$(function() {
// last recaptcha token timestamp in ms (to check if the refresh interval of max 2 minutes is reached in between submit attempts)
recaptchaLoginLastTokenTimestamp = 0;
$("#loginform").on('submit', function(e){
nowTimestamp = new Date().getTime();
// if 100 seconds passed since last recaptcha token => get new token
if(nowTimestamp - recaptchaLoginLastTokenTimestamp > 100000){
// stop submission
e.preventDefault();
// call the recaptcha service to get a token
grecaptcha.ready(function() {
grecaptcha.execute('SITE_KEY', {action:'action_login'})
.then(function(token)
{
// add token value to form
document.getElementById('recaptcha_response_token').value = token;
// update last token timestamp
recaptchaLoginLastTokenTimestamp = new Date().getTime();
// re-submit (with the updated token timestamp, the normal submit process will occur, with validations and other stuff that are bind to submission; no infinite recursive submit calls)
$("#loginform").submit();
});
});
}
});
});
</script>
本文标签: javascriptHow to wait for the reCAPTCHA v3 token synchronouslyStack Overflow
版权声明:本文标题:javascript - How to wait for the reCAPTCHA v3 token synchronously? - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1742229480a2436954.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论