web-dev-qa-db-fra.com

J'ai eu une erreur "timeout-or-duplicate" en utilisant ReCaptcha v3

J'ai reçu un formulaire de contact sur mon site Web sur Laravel et j'aimerais placer un ReCaptcha v3 mais pour l'instant le résultat que j'ai obtenu de la vérification est l'erreur "timeout-or-duplicate".

Pouvez-vous m'aider de A à Z? Je ne sais pas où aller ...

Ma tête :

<script src="https://www.google.com/recaptcha/api.js?render=My_Site_Key"></script>
  <script>
    grecaptcha.ready(function () {
      grecaptcha.execute('My_Site_Key', { action: 'contact' }).then(function (token) {
        var recaptchaResponse = document.getElementById('recaptchaResponse');
          recaptchaResponse.value = token;
      });
    });
  </script>

Le formulaire de contact:

<form action="{{ route('contact.post') }}" id="contact-form" method="post" name="contactForm">
   <input type="hidden" name="_token" id="token" value="{{ csrf_token() }}">
   <input type="hidden" name="recaptcha_response" id="recaptchaResponse">
   <fieldset>
     <div class="col-sm-12">
       <input id="name" name="name" placeholder="Nom*" type="text">
     </div>
     <div class="col-sm-12">
       <input id="email" name="email" placeholder="Email*" type="text">
     </div>
     <div class="col-sm-12">
       <input id="object" name="object" placeholder="Objet*" type="text" autocomplete="off">
     </div>
     <div class="col-xs-12">
       <textarea cols="5" id="message" name="message" placeholder="Votre message...*"></textarea>
     </div>
     <div class="col-xs-12">
       <button class="submit active" id="contact-submit">ENVOYER</button>
     </div>
     <div class="error col-xs-12">
       <h3></h3>
     </div>
     <div class="success col-xs-12">
       <h3>Merci ! Votre message a été envoyé !</h3>
     </div>
   </fieldset>
</form>

Route:

Route::post('/contact', array('as' => 'contact.post', 'uses' => 'ContactController@send'));

Le contrôleur de contact:

<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests;
use Illuminate\Support\Facades\Input;
use Illuminate\Support\Facades\Mail;

class ContactController extends Controller
{
    public function send() {
      $info = array(
          'name' => Input::get('name'),
          'email' => Input::get('email'),
          'object' => Input::get('object'),
          'message' => Input::get('message')
      );
      if($info['name'] == "" || $info['email'] == "" || $info['object'] == "" || $info['message'] == "") {
          return json_encode(['response' => 'Tous les champs doivent être remplis !']);
      }
      if(!filter_var($info['email'], FILTER_VALIDATE_EMAIL)) {
          return json_encode(['response' => 'Vous devez rentrer une adresse e-mail valide !']);
      }
      $ip = Request()->ip();

      // Build POST request:
      $recaptcha_url = 'https://www.google.com/recaptcha/api/siteverify';
      $recaptcha_secret = 'My_Secret_Key';
      $recaptcha_response = $_POST['recaptcha_response'];
      // Make and decode POST request:
      $recaptcha = file_get_contents($recaptcha_url . '?secret=' . $recaptcha_secret . '&response=' . $recaptcha_response);
      $recaptcha = json_decode($recaptcha);
      // Take action based on the score returned:
      if ($recaptcha->score < 0.5) {
          return json_encode(['response' => 'Vous êtes considéré comme Bot/Spammer !', 'score' => $recaptcha->score]);
      }

      Mail::send(['email.html.contact', 'email.text.contact'], ['info' => $info, 'ip' => $ip], function($message) use ($info) {
          $message->to('[email protected]')->subject('Bryan Gossuin | Formulaire de contact');
          $message->replyTo($info['email'], $info['name']);
      });
      return json_encode(['response' => 'success','']);
  }
}

Enfin le javascript

      $('#contact-form').on('submit', function(e) {
          e.preventDefault();
          swal({
              title: "Souhaitez-vous vraiment envoyer ce mail ?",
              icon: "warning",
              buttons: {
                cancel: {
                  text: "Annuler",
                  value: false,
                  visible: true,
                  closeModal: true,
                },
                confirm: "Envoyer",
              }
            })
            .then((value) => {
              if (value) {
                  $.ajax({
                          method: "POST",
                          url: "contact",
                          cache: false,
                          data: $(this).serialize(),
                          dataType: 'json',
                          success: function(json) {
                              console.log(json.score);
                              if (json.response == 'success') {
                                  $('#contact-form').trigger("reset");
                                  swal("E-mail envoyé", "Merci de votre demande !", "success");
                              } else {
                                  swal("Erreur !", json.response, "error");
                              }
                          }
                      }
                  )
               }
            });
      });

La sortie que j'ai obtenue de google est

{
  "success": false,
  "error-codes": [
    "timeout-or-duplicate"
  ]
}

et je m'attends à ce que ce soit

{
  "success": true,
  "score" : x,
  "error-codes": '',
}

Je suppose que le problème est dû au fait que la "méthode post" est utilisée deux fois car lorsque je vérifie directement sur l'API Google pour vérifier le jeton utilisateur, il montre le code mais juste après avoir actualisé la page, il me montre "timeout or duplicate" mais Je ne sais pas comment résoudre ce problème

3
Saku

Comme indiqué dans le documentation cette erreur est causée par:

  1. Le délai de validité du jeton a expiré (après avoir obtenu le jeton de réponse, vous devez le vérifier dans les deux minutes)
  2. Le jeton a été utilisé précédemment. Pour confirmer cela, enregistrez la valeur du jeton avant de l'utiliser (journal des erreurs, fichier local, peu importe)

Ma résolution pour 1, définissez un intervalle qui appelle la fonction set token, afin qu'elle soit actualisée toutes les 2 minutes.

$(document).ready(function() {

            SetCaptchaToken();
            setInterval(function () { SetCaptchaToken(); }, 2 * 60 * 1000);

    });

Résolution pour 2, corrigez votre code :)

4
Jorge

Chaque fois que la page se recharge, vous obtenez un nouveau jeton de Google. Vous ne pouvez utiliser ce jeton qu'une seule fois. D'une manière ou d'une autre, si vous utilisez ce jeton plus d'une fois pour obtenir la réponse de Google Api, vous obtiendrez cette erreur. Vérifiez cette référence d'erreur https://developers.google.com/recaptcha/docs/verify?hl=en

4
Nihar Sarkar

J'ai reçu cela de personnes qui ont double-cliqué sur le bouton Envoyer du formulaire.

2
Matthew Lock

Le problème est ce morceau de code:

<script src="https://www.google.com/recaptcha/api.js?render=My_Site_Key"></script>
  <script>
    grecaptcha.ready(function () {
      grecaptcha.execute('My_Site_Key', { action: 'contact' }).then(function (token) {
        var recaptchaResponse = document.getElementById('recaptchaResponse');
          recaptchaResponse.value = token;
      });
    });
  </script>

Le jeton n'est valide que 2 minutes après que vous execute soit appelé comme indiqué dans le docs :

Remarque: les jetons reCAPTCHA expirent après deux minutes. Si vous protégez une action avec reCAPTCHA, assurez-vous d'appeler execute lorsque l'utilisateur exécute l'action.

Ainsi, si vous passez plus de 2 minutes sur le formulaire de contact, vous obtenez l'erreur de délai d'attente. C'est pourquoi il est recommandé dans la documentation d'appeler uniquement execute si l'utilisateur soumet réellement votre formulaire/prend des mesures. Dans Vanilla JS, cela ressemblerait à ceci:

<script src="https://www.google.com/recaptcha/api.js?render=My_Site_Key"></script>
<script>
  grecaptcha.ready(function() {
      document.getElementById('contact-form').addEventListener("submit", function(event) {
        event.preventDefault();
        grecaptcha.execute('My_Site_Key', {action: 'contact'}).then(function(token) {
           document.getElementById("recaptchaResponse").value= token; 
           document.getElementById('contact-form').submit();
        });
      }, false);
  });
</script>
1
Adam