I'm trying to implement ReCaptcha V3 in my website. But I've been pulling the few hairs that I had left out, as Google doesn't seem to care about docs at all...
And all examples I see are all the simple examples, not really what you would use in prod.
My challenges:
The 1st time I press Submit, the token isn't passed to my PHP for some reason, why not?
The 2nd and further times I always get the same "challenge_ts" back, why? The token is different each time...
Lastly, just wanted to check if my assumption is correct that after a success/fail with score you don't execute JavaScript (as that can be modified), but only PHP code?
Here's my index.html code:
<head>
<script src="https://www.google.com/recaptcha/api.js?render=xxx"></script>
</head>
<body data-spy="scroll" data-target=".fixed-top">
<!-- contact form demo container -->
<div style="max-width: 768px; margin: auto;">
<!-- contact form -->
<div class="card">
<h2 class="card-header">Contact Form</h2>
<div class="card-body">
<form id="myform" class="contact_form" method="post" action="mail.php">
<!-- form fields -->
<div class="row">
<div class="col-md-6 form-group">
<input id="name" name="name" type="text" class="form-control" placeholder="Name">
</div>
<!-- form message prompt -->
<div class="row">
<div class="col-12">
<div class="contact_msg" style="display: none">
<p>Your message was sent.</p>
</div>
</div>
</div>
<div class="col-12">
<input type="submit" value="Submit Form" class="btn btn-success" name="post">
</div>
<!-- hidden reCaptcha token input -->
<input type="hidden" id="token" name="token">
</div>
</form>
</div>
</div>
</div>
<!-- References for the opitional jQuery function to enhance end-user prompts -->
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script>
$('#myform').submit(function() {
grecaptcha.ready(function() {
grecaptcha.execute('xxx', {action: 'homepage'}).then(function(token) {
// console.log(token);
document.getElementById("token").value = token;
console.log(token);
});
});
});
</script>
<script src="form.js"></script>
</body>
</html>
And here's my form.js code:
(function ($) {
'use strict';
var message = $('.contact_msg');
$('.contact_form').submit(function (e) {
e.preventDefault();
// FIRST TIME NOT GRABBING DATA
var token = $('#token').val();
console.log(token);
$.ajax({
type: 'POST',
url: 'form.php',
data: { token:token}
})
.done(done_func)
.fail(fail_func);
});
// Success function
function done_func(response) {
message.fadeIn()
message.html(response);
setTimeout(function () {
message.fadeOut();
}, 50000);
// form.find('input:not([type="submit"]), textarea').val('');
}
// fail function
function fail_func(data) {
message.fadeIn()
message.html(data.responseText);
setTimeout(function () {
message.fadeOut();
}, 20000);
}
})(jQuery);
And here's my form.php code:
<?php
if ($_SERVER["REQUEST_METHOD"] == "POST") {
// print_r("code:".$_POST['token']);
# BEGIN Setting reCaptcha v3 validation data
$url = "https://www.google.com/recaptcha/api/siteverify";
$data = [
'secret' => "xxx",
'response' => $_POST['token'],
'remoteip' => $_SERVER['REMOTE_ADDR']
];
$options = array(
'http' => array(
'header' => "Content-type: application/x-www-form-urlencoded\r\n",
'method' => 'POST',
'content' => http_build_query($data)
)
);
# Creates and returns stream context with options supplied in options preset
$context = stream_context_create($options);
# file_get_contents() is the preferred way to read the contents of a file into a string
$response = file_get_contents($url, false, $context);
# Takes a JSON encoded string and converts it into a PHP variable
$res = json_decode($response, true);
# END setting reCaptcha v3 validation data
print_r($response);
# Post form OR output alert and bypass post if false. NOTE: score conditional is optional, since the successful score default is set at >= 0.5 by Google. Some developers want to be able to control score result conditions, so I included that in this example.
if ($res['success'] == true && $res['score'] >= 0.5) {
// ONLY EXECUTE PHP CODE? NOT LET JAVASCRIPT DECIDE NEXT STEPS?
http_response_code(200);
echo '<p class="alert alert-success">Success!</p>';
} else {
echo '<div class="alert alert-danger">
Error! The security token has expired or you are a bot.
</div>';
}}
Test site:
https://www.citydiscovery.com.au/login/index.html
P.s. what does "action" do in the execute function of grecaptcha? Does it have any purpose?
Ok, not sure what the issue was, but I'm now able to get a new timestamp each time by using a different code example from a YouTube video.
It's very tricky though, and so far have not seen any proper implementation at all.
Still coding, but some advice:
It takes around a second for the execute function to fetch a new token, so I built in a 1 sec delay before the button is enabled again
As mentioned in a comment, use the example ready function, but also call the function when submitting a form
Tokens are supposedly only valid for 2 mins, so there could still be an issue by calling the ready function on page load after 2 mins. Not yet sure how to solve that as calling the function when submitting the form doesn't work the first time
Related
for several days I have been facing the problem that PHP cannot find my index.
What I've tried:
Change the data name in ajax
I added the code from PHP Create.php to create.php (at the beginning and at the end of the code)
Various ajax possibilities
The goal
I want to save an image which I have cropped with (cropper.js), using PHP on a SQL server.
My code:
OnSetData.js
canvas = cropper.getCroppedCanvas({
width:700,
height:700
});
canvas.toBlob((blob) => {
url_img = URL.createObjectURL(blob);
//url_img = blob:https://localhost/a37a7cd8-ad48...
$.ajax(
{
url:'assets/php/PHPCreate.php',
type: 'POST',
data: {'image':url_img},
success:function(output) {
console.log('Upload success: ' + output);
//Upload sucess: <output is empty>
},
error() {
console.log('Upload error');
},
});
}, 'image/png');
PHPCreate.php
if (isset($_POST['save_submit']))
{
$data = $_POST["image"];
//Warning: Undefined array key "image" in ..\assets\php\PHPCreate.php on line ..
echo($data);
}
create.php
<link href="assets/assets/cropperjs-main/dist/cropper.css" rel="stylesheet">
<script src="assets/assets/cropperjs-main/dist/cropper.js"></script>
<script src="assets/js/jquery.min.js"></script>
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
<script src="assets/js/OnSetData.js"></script>
<?php
include './assets/php/PHPCreate.php';
?>
.
.
.
.
<form id="formUpload" action="" method="post" class="form-inline" enctype="multipart/form-data">
<button class="btn btn-primary text-uppercase" role="button" name="save_submit" type="submit">Save</button>
</form>
i think you will open create.php in browser
create.php has a form that sends "save_submit" to izself as a new request
so create.php will be opened again but this time with "save_submit", nothing else, so yes, there is no image, that is correct
now lets look at OnSetData.js:
it makes a separate request to PHPCreate.php with "image", but no "save_submit" so PHPCreate.php will do nothing
to clearify:
the button in the form will make a site navigation
OnSetData.js will make a request on its own
both request are handled in separate
I have two routes:
Route::get('/download/{hash}','DownloadController#exists')->name('exists');
Route::post('/download/{hash}','DownloadController#verify')->name('verify');
Procedure:
Basically the user enters a URL e.g. localhost/download/56iq45agosasa the first route is called and the download.blade.php view is shown. There is a form, where the user has to input a password and when the submit button is clicked, an ajax request is sent to the second (post) route.
The DownloadController#verify returns the json but displays it in a blank window (see screenshot) But it should be returned in the download.blade.php view.
Somehow the form disappears and the ajax success function is not called.
Code snippets Download.blade.php:
#section('content')
<div class="container-fluid mt-5">
<div class="row justify-content-md-center">
<div class="col-md-4">
<form method="POST">
<label for="uploadPassword">Password</label>
<input type="password" name="password" class="form-control" id="uploadPassword" placeholder="Password" required="">
<button id="btn-submit" type="submit" class="btn btn-success mt-2">Submit</button>
{{ csrf_field() }}
</form>
</div>
</div>
</div>
#endsection
#push('scripts')
<script type="text/javascript">
$(".btn-submit").click(function(e){
e.preventDefault();
let password = $("input[name=password]").val();
$.ajax({
type:'POST',
url:"",
data:{
password:password
},
success:function(data){
console.log("I don't get shown");
//alert(data.success + " " + data.matches);
}
});
});
</script>
#endpush
DownloadController:
class DownloadController extends Controller
{
public function exists(Request $request, $hash)
{
// Some private mysql queries
// Return the hash to the download view
return view('download',[
'hash' => $hash
]);
}
public function verify(Request $request, $hash)
{
return response()->json(
['success'=>'Got Simple Ajax Request.']
);
}
}
So there are two things that will help improve this code:
Wrap the listener in a document.ready function to ensure it's attached when the button is available in the page. This is necessary if #push will make the script end up above the form declaration in the page.
Listen for the form submit event so you can capture the submit via any of the possible ways that one can submit a form (e.g. by pressing ENTER on an input)
#push('scripts')
<script type="text/javascript">
$(function () {
$("form").on('submit', function (e) {
e.preventDefault();
let password = $("input[name=password]").val();
$.ajax({
type:'POST',
url:"",
data:{
password:password
},
success:function(data){
console.log("I don't get shown");
//alert(data.success + " " + data.matches);
}
});
});
});
</script>
#endpush
The problem here is that the form gets submitted.
Instead of overriding button click with your jQuery function, use it to change submit handler like so
Prevent Default on Form Submit jQuery
Make sure you put id on the form.
So I'm having some issues with my AJAX form submission and I can't seem to figure out why, below I will be explaining all the steps that I will be taking to attempt to submit the form.
The scripts are enqueued [WORKS]: Ensures that the file holding the ajax request is loaded which it is.
function enqueue_scripts()
{
if (!is_admin()) {
wp_register_script('profile_edit_submit', content_url() . '/mu-plugins/fleishmanhillard/scripts/frontend-profile-edit.js', ['jquery'], '', true);
wp_localize_script( 'profile_edit_submit', 'profile_edit', [
// This will generate the admin URL that we can use on the front-end of our website
'ajax_url' => admin_url('admin-ajax.php'),
// Pass in a custom nonce name for JS
'nonce' => wp_create_nonce('update_profile_validation'),
]);
wp_enqueue_script('profile_edit_submit');
}
}
add_action('wp_enqueue_scripts', 'enqueue_scripts');
This updates the post ID's post_content [WORKS]: On submission, it updates the database content of the right ID.
if (strtolower($_SERVER['REQUEST_METHOD']) === "post") {
// #todo: Make sure that the account is associated with the logged in account on profile edits
// If profile isn't empty, continue
if (!empty($profile)) {
// Grab the content submitted
// #todo: Figure out how to set the $_POST request to a function
$post_content = $_POST['content'];
wp_update_post([
'ID' => $profile->get_id(),
'post_content' => $post_content,
]);
}
}
HTML [WORKS]:
<form action="" id="profile_update" method="POST">
<input type="text" name="content" id="post_content" class="required">
<input type="hidden" name="submitted" id="submitted" value="true" />
<button type="submit"><?= 'Update Post' ?></button>
</form>
PROBLEM: So the submission works but it refreshes the page on submit, but I'd like to move forward with an AJAX submission but I don't know where to begin.. here is what I have, but it's not working correctly.
(function ($) {
$(function($) {
$('.profile_update').on('click', function() {
$.ajax({
type: 'POST',
url: profile_edit.ajaxurl,
data: {
}
});
});
});
})(jQuery);
When I submit the form, it updates the database and also changes the server request from GET to POST.
we have to change the javascript.
(function ($) {
$(function($) {
$('#profile_update').on('submit', function(e) {
$.ajax({
type: 'POST',
url: profile_edit.ajaxurl,
data: $('#profile_update').serialize();
});
return false;
});
});
})(jQuery);
change the html this way
<form action="" method="POST">
<input type="text" name="content" id="post_content" class="required">
<input type="hidden" name="submitted" id="submitted" value="true" />
<button type="button" id="profile_update"><?= 'Update Post' ?></button>
</form>
you are selecting a CSS class by using . in jQuery you need to use #
it should be
$('#profile_update')
I have the following script which, prevents the form from being submitted and then uses ajax to make a call to a page
HERE is my form
<form method="post" action="makeBid.php" name="apply" id="makeBid">
<label for="amount">Bid Amount</label>
<input type="text" id="amount" name="amount" placeholder="Enter Bid Amount"/>
<label for="completionDate">Completion Date</label>
<input type="text" id="completionDate" name="completionDate" placeholder="Completion Date"/>
<label for="apply">Support Your Application</label>
<textarea name="msg" id="msg" class="application" placeholder="Enter A Message To Support Your Application"></textarea>
<button name="apply" id="apply" value="<?php echo $_POST['btnSubmit'] ?>" class="btn btndanger">Apply</button>
</form>
if(isset($_POST['apply'])) {
require_once('../controller/bids.php');
$bid = new Bid();
$bid->setAmount($_POST['amount']);
$amount = $bid->getAmount();
$bid->setDate($_POST['completionDate']);
$date = $bid->getDate();
$bid->setRemarks($_POST['msg']);
$msg = $bid->getRemarks();
echo $bid->processBid($_SESSION['userID'], $_POST['apply'],$amount, $date, $msg);
}
And then my Jquery and AJAX script which prevents default behavior.
$(function () {
var form = $('#makeBid');
var formMessages = $('#formResult');
// Set up an event listener for the contact form.
$(form).submit(function (e) {
// Stop the browser from submitting the form.
e.preventDefault();
// Serialize the form data.
var formData = $(form).serialize();
// Submit the form using AJAX.
$.ajax({
type: 'POST',
url: $(form).attr('action'),
data: formData
}).done(function (response) {
// Make sure that the formMessages div has the 'success' class.
$(formMessages).removeClass('error').addClass('success');
// Set the message text.
$(formMessages).html(response); // < html();
// Clear the form.
$('').val('')
}).fail(function (data) {
// Make sure that the formMessages div has the 'error' class.
$(formMessages).removeClass('success').addClass('error');
// Set the message text.
var messageHtml = data.responseText !== '' ? data.responseText : 'Oops! An error occured and your message could not be sent.';
$(formMessages).html(messageHtml); // < html()
});
});
});
</script>
The console error im getting is uncaught reference error function is not defined in the first line of my script. As far as I can tell everything looks alright. Would greatly appreciate a second pair of eyes / opinion to scan over my script.
Much appreciated
It looks ok!
Just check if you opened the <script> tag properly, because in the example there is not present.
If you can copy the error and post here could be more usefull !
Two things wrong here:
You PHP code needs to begin with <?php to separate from your HTML
Your ajax response won't be correct because the HTML form is also being sent in the response. You need to either place form action script at another file by itself. Or you need to exclude the HTML form by putting in the else statement of your if(isset($_POST['apply']))
I'm hoping for a little help with a rather intractable problem. For starters, I'll explain the problem, because it almost seems like it may be a conceptual one, then I will post some code samples to illustrate.
I have developed an application using HTML and JQuery mobile, using mySQL and PHP for the back-end. I am using phonegap to port it over to the phone. The first page (login page) loads fine on the phone, in chrome, and in DW live view. However, on the phone, when I try to interact with the server in any way (i.e. clicking the login button), I get the following error:
Network error has occurred
SyntaxError: Unexpected token <
Now, by eliminating the first "<" character in my php file (i.e. from the "< ?php" starting tag), the error becomes the following, as I expected:
Network error has occurred:
SyntaxError: Unexpected token ?
Since the first character of the script is now "?", it proves the first character of the script is the problem. This clearly demonstrates that phonegap is not playing nice with my PHP implementation, and I have no idea why. Like I mentioned earlier, the application works flawlessy when viewed in Google Chrome browser, or the DW Live view.
I am using JQuery "$.ajax()" function to send AJAX requests to the server, here is the code:
First, a view of the first page of the JQuery mobile (login):
<div data-role="page" id="login_page">
<div data-role="header">
<h1>Login Page2</h1>
</div>
<div data-role="content">
<form id="login_form">
<div data-role="fieldcontain">
<label for="login_email">Email:</label>
<input type="email" name="login_email" id="login_email" value="" />
<label for="login_pass">Password:</label>
<input type="password" name="login_pass" id="login_pass" value="" />
<h4 id="login_notification"><?php echo 'Notifications will appear here...'; ?></h4>
<button data-theme="b" id="login_submit" type="button">Login</button>
</div>
</form>
</div>
<div data-role="footer">
<center>
<div data-role="controlgroup" data-type="horizontal">
Don't have an account? Register here.
</div>
</center>
</div>
</div>
Now, the relevant Javascript/JQuery:
// Login page handler
$(document).on('pageshow', '#login_page', function() {
$("#login_notification").text("page loaded");
// Check to see if a user account is loaded in, if not create one set to dummy variables
if (localStorage.getItem("logged_user") === null) {
var userInfo = {
"user_id": -1,
"logged_in": false,
};
localStorage.setItem("logged_user", JSON.stringify(userInfo));
}
$(document).on('click', '#login_submit', function(){
$("#login_notification").text("button clicked");
var formData = $("#login_form").serialize();
$.ajax({
type: "POST", // Method of sending data to server
url: "php_scripts/login_handle.php", // php script being sent to
cache: false, // requested pages won't be cached by server
data: formData, // data to be sent to server
dataType: "json", // data type to be received back from server
success: onLoginSuccess, // function to call on success
error: onError // function to call on error
});
return false;
//alert('Testing alert!');
});
});
And finally, the PHP script, which I don't think is itself the problem (like I said, i suspect the first character of the script, regardless of what it is, is throwing the error):
<?php
include_once 'db_connect.php';
$valid_login = false;
$error_msg = '';
// Check to see if necessary fields are set
if (isset($_POST['login_email'], $_POST['login_pass'])) {
// Extract data
$email = $_POST['login_email'];
$raw_pass = $_POST['login_pass'];
// Check the database to see if the user exists
$check_stmt = $mysqli->prepare("SELECT id, username, password, salt FROM members WHERE email = ? LIMIT 1");
if ($check_stmt) {
if ($check_stmt->bind_param('s', $email)) {
if ($check_stmt->execute()) {
$check_stmt->store_result();
// Get variables from result
$check_stmt->bind_result($user_id, $username, $db_password, $salt);
$check_stmt->fetch();
// Append the salt to the raw password and hash them together
$password = hash('sha512', $raw_pass . $salt);
// Check if user w/ email address exists
if ($check_stmt->num_rows == 1) {
// Check to see if passwords match
if ($password == $db_password) {
// If they do, set login boolean to true
$valid_login = true;
}else {
$error_msg .= 'Invalid email or password';
}
}else {
$error_msg .= 'No user with that email address. ';
}
}else {
$error_msg .= 'Database error (execute, prepare). ';
}
}else {
$error_msg .= 'Database error (bind, prepare). ';
}
}else {
$error_msg .= 'Database error (check, prepare). ';
}
}else {
$error_msg .= 'Please fill out the required fields. ';
}
$output = array('valid' => $valid_login, 'error_msg' => $error_msg, 'user_id' => $user_id);
echo json_encode($output);
?>
Please, anyone with some phonegap experience, please help me figure out why it's not working! Thank you!
You've to change the ajax 'url' to the absolute url of the php script that resides on your webserver. Not a php script inside your phonegap application
$.ajax({
type: "POST", // Method of sending data to server
url: "http://domain.com/php_scripts/login_handle.php", // php script being sent to
cache: false, // requested pages won't be cached by server
data: formData, // data to be sent to server
dataType: "json", // data type to be received back from server
success: onLoginSuccess, // function to call on success
error: onError // function to call on error
});