I'm trying to integrate ReCaptcha (v3) with a SvelteKit form. I'm using a regular form action. The problem is that I can't figure out how to pass the token that's generated from ReCaptcha to Sveltekit's server side.
In theory it's easy: submit the form, call ReCaptcha to get the token, and add it to the form data. However I can't figure out how to add the token to the form data, because the function that get the token is asynchronous.
Inside +page.svelte
I have the following. How can I get the value of the Recaptcha token in the use:enhance
function?
<script>
let token;
function checkCaptcha() {
grecaptcha.ready(function() {
grecaptcha.execute(key, { action: "submit" }).then(function(t) {
token = t;
console.log(token); // this works, we get the token
});
});
}
</script>
<svelte:head>
<script src="https://www.google.com/recaptcha/api.js?render={key}" async defer></script>
</svelte:head>
<form method="POST" use:enhance={async ({ formData }) => {
checkCaptcha();
formData.append("token", token); // the token doesn't get here
return async ({ result, update }) => {
update();
};
}}>
<!-- form content -->
</form>
It looks like the intended approach is to prevent the submission; to still use the regular SvelteKit form action process, you could try to trigger another submission once you have the token.
Something along these lines:
<script>
import { enhance } from '$app/forms';
/** @import { SubmitFunction } from '@sveltejs/kit'; */
let token = null;
/** @type {SubmitFunction} */
const onSubmit = event => {
if (token == null) {
event.cancel();
grecaptcha.ready(async () => {
token = await grecaptcha.execute('...', { action: 'submit' });
setTimeout(() => event.formElement.requestSubmit());
});
return;
}
event.formData.append('token', token);
// [possibly clear token again]
};
</script>
<form method="post" use:enhance={onSubmit}>
...
<button>Submit</button>
</form>
(You need requestSubmit
rather than submit
to trigger validation and event handlers like the one attached by enhanced
. The setTimeout
may not be necessary if the library already defers the callback into another event loop.)