I got a contact form on my website on Laravel and I'd like to place a ReCaptcha v3 but for now the result I got from the verification is the error "timeout-or-duplicate".
Can you help me from A to Z ? I don't know where to go...
My head :
<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>
The contact form :
<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'));
The Contact Controller :
<?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('contact#bryangossuin.be')->subject('Bryan Gossuin | Formulaire de contact');
$message->replyTo($info['email'], $info['name']);
});
return json_encode(['response' => 'success','']);
}
}
Finaly the 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");
}
}
}
)
}
});
});
The output I got from google is
{
"success": false,
"error-codes": [
"timeout-or-duplicate"
]
}
and I expect it to be
{
"success": true,
"score" : x,
"error-codes": '',
}
I guess the problem is because the « method post » is used two times because when I Check directly
On the API Google to verify the user token it show le thé code but right after I refresh the page it show me « timeout or duplicate » but I dont know how to fix this
I got this from people double clicking the submit button on the form.
As stated in the documentation this error is caused by:
Validity time of the token expired (After you get the response token, you need to verify it within two minutes)
Token has been used previously. To confirm that, log the token value before is used (error log, local file, whatever)
My resolution for 1, set an interval that calls the set token function, so it is refreshed every 2 minutes.
$(document).ready(function() {
SetCaptchaToken();
setInterval(function () { SetCaptchaToken(); }, 2 * 60 * 1000);
});
Resolution for 2, fix your code :)
The problem is this piece of 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>
The token is only valid for 2 minutes after you execute is called as stated in the docs:
Note: reCAPTCHA tokens expire after two minutes. If you're protecting an action with reCAPTCHA, make sure to call execute when the user takes the action.
Thus, if you spend more then 2 minutes on the contact-form, you get the timout error,. Thats why its recommended in the docs to only call execute if the user actually submits your form / takes action. In vanilla JS it would look like this:
<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>
Every time the page reloads you get a new token from google . You can use that token only once . Somehow if you are using that token more than once to get the response from google Api , you will get that error . Check this error reference https://developers.google.com/recaptcha/docs/verify?hl=en
I been googling looking for answers specifically similar to your use case.
reCaptcha V3 does not have reset API.
I solve the problem by when Password or Email authentication failed on your side, execute this again on your AJAX if failed. So that the value get replace with new g-token without reloading the site again, since following Google Documentation like me, the script execute on ready at your "signin page"
grecaptcha.ready(function() {
grecaptcha.execute('abhkdfhlasdfhldafhlashflasdhl', {action: 'submit'}).then(function(token) {
document.getElementById('g-token').value = token;
});
});
The issue is likely caused because the script is running more than once.
Is there anywhere else in the code that could be submitting the form more than once?
I had a similar issue and a simple console log in the results part of the JS showed that it was being printed twice i.e. the form was submitting twice.
If it is not a code issue, a user may be double clicking the button. You could do a simple on click event to disable the button and this would remove the error.
Related
i have this login form
<form autocomplete="off" id="login_form">
<div class="login-wrapper">
<input required type="text" class="login-input" name="email" id="email" placeholder="email">
<span class="fas fa-envelope mail_name-email"></span>
<span class="err_output err_email"></span>
</div>
<div class="login-wrapper">
<input required type="password" class="login-input" name="pwd" id="pwd" placeholder="password">
<span class="fas fa-lock pwd_password"></span>
<span class="err_output err_pwd"></span>
</div>
<input type="submit" class="login_btn" id="login_btn" name="login" value="log in">
</form>
the submission is handled using jquery, like so
$(document).ready(function() {
$(document).on("submit", "#login_form", function() {
Login();
//send values to post
const mail = document.getElementById("email").value;
const pwd = document.getElementById("pwd").value;
$.ajax({
type: "POST",
url: "./inc/login.php",
data: {
email: mail,
password: pwd
}
});
return false;
});
});
so it works well but i wanted to do all the validation on the serverside particluarly in the login.php file included in the url within the jquery code because the data entered is sensitive and i cannot just redirect usin javascript. So even before i started the validation i tried a redirect to another page after the form was submitted but it wouldn't work, i tried header("Location: ../main.php") and echo "<script>location='../dashboard.php'</script>"; but on the console all i saw was this
jquery.js:9837 XHR finished loading: POST "http://localhost/My%20portfolio/admin/inc/login".
i have even included an action attribute on my form pointing to the action page but it doesn't work, this is the only way i can proceed with validation otherwise i am stuck, i dont know what's wrong
You can't use a redirect in PHP on an ajax call. You need to return something to the JS page and redirect from there. For example, your PHP can return a json object with the status and the URL to forward to.
You can output something like this:
{
"status" : "success",
"url" : "http://www.example.com/url-to-redirect"
}
Or if it fails
{
"status" : "error",
"message" : "Error message to show"
}
Then in your javascript, check for the answer and validate the status
$.ajax({
type: "POST",
url: "./inc/login.php",
data: {
email: mail,
password: pwd
},
dataType: "json"
}).done(function( data ) {
if (data.status === "success") {
window.location.href = data.url;
}
else if (data.status === "error") {
alert(data.message);
}
});
In your PHP script you need to output something like an array.
So in your PHP validation, if everything is validated, you can simply do
echo json_encode(array('status' => 'success', 'url' => 'http://www.example.com/url-to-redirect'));
But if it fails:
echo json_encode(array('status' => 'error', 'message' => 'Error message to show'));
I suggest you read more on json_encode and ajax calls with PHP.
I have a form with some fields (first name, last name and profile picture) that looks like this:
<form>
<div class="row">
<div class="col-lg-6">
<label class="control-label">First Name:</label>
<input class="form-control" type="text" placeholder="Enter first name.." id="firstName">
</div>
<div class="form-group col-lg-6">
<label class="control-label">Last Name:</label>
<input class="form-control" type="text" placeholder="Enter last name.." id="lastName">
</div>
</div> <!-- /row -->
<div class="row">
<div class="form-group col-lg-6">
<label class="control-label">profile picture:</label>
<div class="dropzone dropzone-previews" id="profilbildDropzone">
<div class="fallback">
<input name="file" type="file" multiple="multiple">
</div>
</div>
</div>
</div> <!-- /row -->
<hr />
<div class="form-action">
<button type="button" class="btn btn-primary waves-effect waves-light" id="sweet-ajax">Submit Data</button>
</div>
</form>
When the user clicks on submit data I call a Sweet-Alert dialog that asks if the user is sure about the action he tries. If he clicks yes I want to submit the data via AJAX to an PHP script that does all the rest (storing the image on the server, save the data in my database and so on):
<script type="text/javascript">
SweetAlert.prototype.init = function () {
//Ajax
$('#sweet-ajax').click(function () {
swal({
title: "Sure?",
text: "Clicking on yes submits the data!",
type: "warning",
showCancelButton: true,
closeOnConfirm: false,
confirmButtonText: "Yes!",
cancelButtonText: "Cancel",
showLoaderOnConfirm: true,
confirmButtonClass: 'btn btn-success',
cancelButtonClass: 'btn btn-danger m-l-10',
preConfirm: function(givenData){
return new Promise(function(resolve, reject) {
setTimeout(function(){
inputNameFirst = document.getElementById("firstName").value;
inputNameLast = document.getElementById("lastName").value;
resolve()
}, 2000)
})
},
allowOutsideClick: false
}).then(function(givenData){
$.ajax({
type: "post",
url: "../assets/php/addUser.php",
data: {done: "success", fN: inputNameFirst, ln: inputNameLast},
dataType: 'JSON',
success: function(data) {
if(data.status === 'success'){
swal({
type: 'success',
title: 'Good job!',
html: 'all saved now!'
})
}else if(data.status === 'error'){
swal({
type: 'error',
title: 'Oh No!!',
html: 'Nope sorry, there was an error: <br /><pre>'+ JSON.stringify(data) + '</pre>'
})
}
}
})
}, function(dismiss) {
// dismiss can be 'overlay', 'cancel', 'close', 'esc', 'timer'
if (dismiss === 'cancel') {
swal(
'Got you!',
'Nothing changed. Good luck.',
'error'
)
}
})
});
},
//init
$.SweetAlert = new SweetAlert, $.SweetAlert.Constructor = SweetAlert
}(window.jQuery),
//initializing
function ($) {
"use strict";
$.SweetAlert.init()
}(window.jQuery);
</script>
I also set up the DropZone on my site so that I can also have optional fields in my form (from here):
jQuery(document).ready(function() {
$("div#profilbildDropzone").dropzone({
//store images in this directory
url: "../assets/images/uploads",
dictDefaultMessage: "Drop image here or click.",
autoProcessQueue: false,
maxFiles: 1,
uploadMultiple: true,
parallelUploads: 100,
// The setting up of the dropzone
init: function() {
var myDropzone = this;
// First change the button to actually tell Dropzone to process the queue.
this.element.querySelector("button#sweet-ajax").addEventListener("click", function(e) {
// Make sure that the form isn't actually being sent.
e.preventDefault();
e.stopPropagation();
myDropzone.processQueue();
});
// Listen to the sendingmultiple event. In this case, it's the sendingmultiple event instead
// of the sending event because uploadMultiple is set to true.
this.on("sendingmultiple", function() {
// Gets triggered when the form is actually being sent.
// Hide the success button or the complete form.
});
this.on("successmultiple", function(files, response) {
// Gets triggered when the files have successfully been sent.
// Redirect user or notify of success.
});
this.on("errormultiple", function(files, response) {
// Gets triggered when there was an error sending the files.
// Maybe show form again, and notify user of error
});
}
});
});
But I do not get it how I upload the picture or give it so my php script.
In this line:
this.on("sendingmultiple", function() {
// Gets triggered when the form is actually being sent.
// Hide the success button or the complete form.
});
I'm supposed to upload the picture or what? Can I call this function inside my SWAL function or pass the data to my SWAL function where it gets send to the PHP script?
Sadly I haven't found any usable example that gives me a clear hint on how I could solve this specific problem.
You are not written logic send data via ajax to server side (i.e PHP). On click of the submit button you are telling Dropzone.js to processQueue(). But that itself will not post the data to the server via ajax.
On sendingmultiple event you need to get form data and assign it to formObject, then DropzoneJS will take care of posting data along file/image to the PHP.
init: function() {
var myDropzone = this;
// First change the button to actually tell Dropzone to process the queue.
this.element.querySelector("button#sweet-ajax").addEventListener("click", function(e) {
// Make sure that the form isn't actually being sent.
e.preventDefault();
e.stopPropagation();
myDropzone.processQueue();
});
// Listen to the sendingmultiple event. In this case, it's the sendingmultiple event instead
// of the sending event because uploadMultiple is set to true.
this.on("sendingmultiple", function() {
// Append all form inputs to the formData Dropzone will POST
var data = $form.serializeArray();
$.each(data, function (key, el) {
formData.append(el.name, el.value);
});
});
}
Next on the PHP server side get the posted data and files/images likes this.
<?php
//get form data
echo '<pre>';print_r($_POST);echo '</pre>';
//get posted files
echo '<pre>';print_r($_FILES);echo '</pre>';
exit;
Next write your logic upload files/images and also updating the database with posted data.
Also check I have written detailed tutorials on how to upload images with form data on button click using DropzoneJS and PHP via ajax.
Ajax Image Upload Using Dropzone.js with Normal Form Fields On Button Click Using PHP
This is my core script of login controller this means all the sanitization and verification of user is done.
code inside LoginController.php
if ( password_verify($valid['pass'], $pass) ) {
// My session stuffs
header('Content-Type: application/json');
echo json_encode (array(
'result' => 'success',
'msg' => 'Login successful..! You will be redirected in a moment'
));
redirect('user/profile'); //Ajax redirect only works when I remove this.
exit;
}
Code inside login.php
try {
LoginController::UserLogin(); // Calling my method inside my controller
} catch (Exception $e) {
echo json_encode(array(
'error' => array(
'msg' => $e->getMessage()
),
), JSON_PRETTY_PRINT);
header('Content-Type: application/json');
exit;
}
<form id="login_form" action="<?php echo htmlspecialchars(pathinfo($_SERVER['PHP_SELF'], PATHINFO_FILENAME)); ?>" class="form" method="POST">
<div class="form-group">
<input type="text" name="login_username" placeholder="Enter username" class="form-control" />
</div>
<div class="form-group">
<input type="password" name="login_pass" placeholder="Enter password" class="form-control" />
</div>
<div class="form-group">
<input type="text" name="login_security" placeholder="Enter Security 25 * 2" class="form-control" />
</div>
<button class="btn btn-green" id="login-btn">Login</button>
</form>
My ajax part
$('#login_form #login-btn').on('click', function (e) {
e.preventDefault();
$.ajax({
url : $('#login_form').attr('action'),
type: 'POST',
dataType: 'json',
data: $('#login_form').serialize(),
success: function(data) {
console.log(data);
if (data.error) {
$('.error').html('<div class="alert alert-danger">' + data.error.msg + '</div>');
}
if ( data.result === 'success' ) {
$('.error').html('<div class="alert alert-unknown">' + data.msg + '</div>');
setTimeout('window.location.href = "user/profile.php";',3000);
}
}
});
});
My problem: Everything works fine until I use ajax. This means the page without Ajax shows error and everything is fine. But when I use ajax the errors are returned but when the login details are correct (Valid details) the page gets stuck(The page is not redirected).
When I check the developer console what I see is this 302 for login page and the redirect is not taking place(But it works when I remove the header from the Login controller form this causes no redirect when the user does not have JavaScript disabled).
Try this and see if it works:
In the LoginController.php: REMOVE the redirect after the echo part.
In the Login.php, put the header BEFORE the echo part.
In the Ajax part, chage the success part like this:
success: function(data) {
console.log(data);
if (data.error) {
$('.error').html('<div class="alert alert-danger">' + data.error.msg + '</div>');
}
if ( data.result === 'success' ) {
$('.error').html('<div class="alert alert-unknown">' + data.msg + '</div>');
// -> here
// correct setTimeout function
setTimeout(function(){
window.location = "user/profile.php";
},3000);
}
}
there must be a redirect set at the start of your controller , if ur using the FuelPhp please check for a function called "before", then include your exception function inside
public function before()
{
parent::before();
$login_exception = array("login");
parent::check_permission($login_exception);
}
I create an mobile app with phonegap and I dont know how smart it is but I want to user to reg. on my app with their phone number and PIN. Users will download app, type phone number and get PIN via text message (SMS) (twillio api)... Now I have basic problem how to run an laravel php code with ajax cross domain.
I do:
HTML+JS+ajax:
<body class='bg-blue'>
</br>
<h1 class="text-center">AgroAgro</h1>
</br>
<h4 class="text-center">Register manager account and add workers.</h4>
</br>
<div class="col-xs-12"><p>Your Phone Number</p></div>
<div class="col-xs-12">
<input class="form-control" placeholder="Your Phone Number"/>
</div>
<div class="col-xs-12"></br></div>
<div class="col-xs-12 text-center">
<button id="createPin" class="btn btn-success btn-lg">FREE sign up</button>
</div>
<footer>We will send you a PIN code via text message immediately to login and get started. We will never share or span your phone number.</footer>
<script>
$(function() {
$('#createPin').click(function(e) {
e.preventDefault();
$.ajax({
url: "localhost:8000/createPin",
type: "POST",
async: true,
data: { phoneNumber:$("#createPin").val()},
dataType: "json",
success: function(data) {
console.log(data);
//do something
},
error: function (data) {
console.log(data);
}
});
});
});
</script>
on backend side I write php with laravel framework:
Route::put('createPin', function()
{
$phoneNumber = Input::get('phoneNumber');
$pin = rand(1000,9999);
$user = new User;
$user->phoneNumber = $phoneNumber;
$user->pin = $pin;
// this line loads the library
require('/path/to/twilio-php/Services/Twilio.php');
$account_sid = 'AC6333333911376fghhbced190522da587';
$auth_token = '[AuthToken]';
$client = new Services_Twilio($account_sid, $auth_token);
$client->account->messages->create(array(
'To' => $phoneNumber,
'From' => "+18702802244",
'Body' => "PIN code",
'MediaUrl' => "Your pin code is ".$pin,
));
$user->save();
return $users;
});
When I click on button with IDcreatPin I get this error:
XMLHttpRequest cannot load localhost:8000/createPin. Cross origin requests are only supported for protocol schemes: http, data, chrome-extension, https, chrome-extension-resource.
How I can solve this problem? Also what you think about idea of PIN instead password?
Recently I swapped out a GoDaddy hosting and directing the requested DNS server to another mine.
The direction is pointing into a folder.
What happens is that I'm trying to make a validation by ajax login before submitting the form to actually login.
The AJAX calls and executes a php but data is not passed by POST as if they were lost in the request.
However, PHP is running in the AJAX request and even returns a message as I wanted, but the data is sent through ajax as if they got anything.
I tried to send the form by post Direct and caused the same error. I'm believing in the possibility that the POST is getting lost in directing the DNS. Is it possible?
Anyway, I'm leaving the HTML, AJAX and PHP:
Note: I'm using CodeIgniter.
HTML:
<?php echo form_open('usuarios/login', array('method' => 'post', 'id' => 'form-login')) ?>
<div class="boxform">
<label for="loginnome">Usuário ou E-mail:</label>
<input type="text" name="loginnome" required id="loginnome" placeholder="Usuário ou E-mail" title="Usuário ou E-mail">
</div>
<div class="boxform">
<label for="loginsenha">Senha:</label>
<input type="password" name="loginsenha" required id="loginsenha" placeholder="Senha" title="Senha">
</div>
<div class="boxform">
<input type="submit" class="button submit-claro" value="Logar" title="Logar">
</div>
<?php echo form_close(); ?>
AJAX:
$('#form-login').validate({
rules: {
loginnome: {
required:true,
maxlength:100
},
loginsenha:{
required:true,
maxlength:30
}
},
submitHandler: function( form ){
$.ajax({
type : "POST",
url : get_url()+"usuarios/validar_login",
dataType : 'json',
data : {
'usuario' : $('#loginnome').val(),
'senha' : $('#loginsenha').val()
},
success : function(data){
console.log(data);
if (data.usuario === false) {
$('.msg_error p').show();
$('.msg_error p').html('');
$('.msg_error p').html('Usuario inválido');
$('#loginnome').val('').focus();
}
else {
if (data.senha === false) {
$('.msg_error p').show();
$('.msg_error p').html('');
$('.msg_error p').html('Senha incorreta');
$('#loginnome p').val('').focus();
}
else {
inicia_sessao(data.url);
}
}
}
});
//return false;
}
});
PHP:
public function validar_login()
{
$usuario = $this->input->post('usuario');
$senha = $this->input->post('senha');
$return = $this->usuario->valida_login($usuario,$senha);
$return['url'] = site_url();
echo json_encode($return); die;
}
You may have to contact your provider to resolve this as it is directly / indirectly under their control.