DarkGhostHunter / Captchavel

Integrate reCAPTCHA into your Laravel app better than the Big G itself!
MIT License
95 stars 17 forks source link

Form submission that results in file download not resetting reCAPTCHA key #15

Closed SeinopSys closed 4 years ago

SeinopSys commented 5 years ago

Let me preface this by saying thank you for the easy to use library, your work is very much appreciated. I just ran into a small issue while integrating it into my site: I have a form that causes a file download in the current window instead of navigating to a different page once it's submitted. This is a problem because the _recaptcha field is only created once on page load, so subsequent attempts to send the form will fail with a FailedRecaptchaException due to the duplicate submission of the same token.

I devised the solution below with as few changes from the original script as possible, you'd be welcome to incorporate this change into the package if you feel like it, or use a different solution entirely, I just wanted to bring this issue to your attention. With the following change it simply calls the key fetching sequence each time the form is submitted alongside the initial call on page load. This solved the issue in my case.

<script src="https://www.google.com/recaptcha/api.js?render={{ $key }}&onload=captchavelCallback" defer></script>
<script>
    // Start Captchavel Script
    window.captchavelCallback = function () {
        let site_key = "{{ $key }}";

        if (site_key === '') {
            console.error("You haven't set your Site Key for reCAPTCHA v3. Get it on https://g.co/recaptcha/admin.");
            return;
        }

        Array.from(document.getElementsByTagName('form'))
            .filter((form) => form.dataset.recaptcha === 'true')
            .forEach((form) => {
                let action = form.action.includes('://') ? (new URL(form.action)).pathname : form.action;
                const getKey = () => {
                    grecaptcha.execute(site_key, {
                        action: action
                                .substring(action.indexOf('?'), action.length)
                                .replace(/[^A-z\/_]/gi, '')
                    }).then((token) => {
                        if (token) {
                            let child = document.createElement('input');
                            child.setAttribute('type', 'hidden');
                            child.setAttribute('name', '_recaptcha');
                            child.setAttribute('value', token);
                            form.appendChild(child);
                        }
                    });
                };
                getKey();
                form.addEventListener('submit', getKey);
            });
    };
</script>
DarkGhostHunter commented 5 years ago

Wow, never thought that. Gonna add it as soon as possible since I have the same problem on some places.

DarkGhostHunter commented 4 years ago

Okay. That script will require two times the key even if your are inside the expiration window.

I modified the script to retrieve the key on submission on the newest version. It should work nevertheless.