recaptchasveltekit

Trying to integrate ReCaptcha (v3) with a SvelteKit form


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>


Solution

  • 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.)