Hi,
I've been trying to implement reCAPTCHA into my cakePHP project and have faced nothing but hordes of issues such as assessment fields required and invalid site key.
I saw a post that someone said to just remake a new site key so I have done that, it is v2 tickbox reCAPTCHA. I made sure I am using the right key, on the console it has a tick for Frontend/Backend integration. I have my code set up in the following:
Within the <head> tag of add.php
<script src="https://www.google.com/recaptcha/enterprise.js" async defer></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
document.getElementById('enquiryForm').addEventListener('submit', function(event) {
event.preventDefault();
grecaptcha.enterprise.ready(function() {
grecaptcha.enterprise.execute('SITE-KEY', {action: 'submit'}).then(function(token) {
var form = document.getElementById('enquiryForm');
var recaptchaToken = document.createElement('input');
recaptchaToken.setAttribute('type', 'hidden');
recaptchaToken.setAttribute('name', 'g-recaptcha-response');
recaptchaToken.setAttribute('value', token);
form.appendChild(recaptchaToken);
form.submit();
});
});
});
});
body of add.php
<body>
<div class="container">
<div class="form-box">
<?php
echo $this->Form->create($enquiry, [
'id' => 'enquiryForm',
'type' => 'post'
]);
$this->Form->unlockField('g-recaptcha-response');
?>
<fieldset>
<legend><?= __('Contact Us') ?></legend>
<div class="form-row">
<div class="form-group">
<?= $this->Form->control('first_name', ['label' => 'First Name']) ?>
</div>
<div class="form-group">
<?= $this->Form->control('last_name', ['label' => 'Last Name']) ?>
</div>
</div>
<div class="form-row">
<div class="form-group">
<?= $this->Form->control('email', ['label' => 'Email']) ?>
</div>
<div class="form-group">
<?= $this->Form->control('type', [
'label' => 'Type',
'type' => 'select',
'options' => [
'Product Inquiry' => 'Product Inquiry',
'Order Status' => 'Order Status',
'Technical Support' => 'Technical Support'
]
]) ?>
</div>
</div>
<div class="form-group">
<?= $this->Form->control('message', ['label' => 'Message', 'type' => 'textarea']) ?>
</div>
</fieldset>
<div class="g-recaptcha" data-sitekey="SITE-KEY"></div>
<br/>
<?= $this->Form->button(__('Submit')) ?>
<?= $this->Form->end() ?>
</div>
</div>
</body>
</html>
within EnquiriesController:
create assessment Taken from documentation:
use Google\Cloud\RecaptchaEnterprise\V1\RecaptchaEnterpriseServiceClient;
use Google\Cloud\RecaptchaEnterprise\V1\Event;
use Google\Cloud\RecaptchaEnterprise\V1\Assessment;
use Google\Cloud\RecaptchaEnterprise\V1\TokenProperties\InvalidReason;
/**
* Create an assessment to analyse the risk of a UI action.
* @PARAM string $recaptchaKey The reCAPTCHA key associated with the site/app
* @PARAM string $token The generated token obtained from the client.
* @PARAM string $project Your Google Cloud project ID.
* @PARAM string $action Action name corresponding to the token.
*/
function create_assessment(
string $recaptchaKey,
string $token,
string $project,
string $action
๐ void {
// Create the reCAPTCHA client.
// TODO: Cache the client generation code (recommended) or call client.close() before exiting the method.
$client = new RecaptchaEnterpriseServiceClient();
$projectName = $client->projectName($project);
// Set the properties of the event to be tracked.
$event = (new Event())
->setSiteKey($recaptchaKey)
->setToken($token);
// Build the assessment request.
$assessment = (new Assessment())
->setEvent($event);
try {
$response = $client->createAssessment(
$projectName,
$assessment
);
// Check if the token is valid.
if ($response->getTokenProperties()->getValid() == false) {
printf('The CreateAssessment() call failed because the token was invalid for the following reason: ');
printf(InvalidReason::name($response->getTokenProperties()->getInvalidReason()));
return;
}
// Check if the expected action was executed.
if ($response->getTokenProperties()->getAction() == $action) {
// Get the risk score and the reason(s).
// For more information on interpreting the assessment, see:
// https://cloud.google.com/recaptcha-enterprise/docs/interpret-assessment
printf('The score for the protection action is:');
printf($response->getRiskAnalysis()->getScore());
} else {
printf('The action attribute in your reCAPTCHA tag does not match the action you are expecting to score');
}
} catch (exception $e) {
printf('CreateAssessment() call failed with the following error: ');
printf($e);
}
}
public function add()
{
// Set the path to the service account key file
$credentialsPath = ':\PATH-TO-APPLICATION-DEFAULT-CREDS.JSON';
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentialsPath);
// Debug: Log the credentials path
$this->log('GOOGLE_APPLICATION_CREDENTIALS: ' . getenv('GOOGLE_APPLICATION_CREDENTIALS'), 'debug');
// Check if the credentials file exists
if (!file_exists($credentialsPath)) {
$this->log('Credentials file not found: ' . $credentialsPath, 'error');
$this->Flash->error(__('Credentials file not found.'));
return;
}
$enquiry = $this->Enquiries->newEmptyEntity();
if ($this->request->is('post')) {
$recaptchaToken = $this->request->getData('g-recaptcha-response');
$recaptchaKey = 'SITE-KEY';
$project = 'PROJECT-ID';
$action = 'submit'; // Ensure this matches the action in the frontend
// Debug: Check if reCAPTCHA token is received
$this->log('reCAPTCHA token: ' . $recaptchaToken, 'debug');
try {
// Validate reCAPTCHA
$this->log('Calling create_assessment function', 'debug');
$recaptchaResponse = $this->create_assessment($recaptchaKey, $recaptchaToken, $project, $action);
// Debug: Log reCAPTCHA response
$this->log('reCAPTCHA response: ' . json_encode($recaptchaResponse), 'debug');
if ($recaptchaResponse && $recaptchaResponse->success == true) {
// Debug: Check if reCAPTCHA validation passed
$this->log('reCAPTCHA validation passed', 'debug');
// Process form data
$this->log('Processing form data', 'debug');
$enquiry = $this->Enquiries->patchEntity($enquiry, $this->request->getData());
// Debug: Check form data
$this->log($this->request->getData(), 'debug');
if ($this->Enquiries->save($enquiry)) {
$this->Flash->success(__('The enquiry has been saved.'));
return $this->redirect(['action' => 'index']);
} else {
$this->Flash->error(__('The enquiry could not be saved. Please, try again.'));
// Debug: Log validation errors
$this->log($enquiry->getErrors(), 'debug');
}
} else {
$this->Flash->error(__('reCAPTCHA verification failed.'));
// Debug: Log reCAPTCHA errors
$this->log('reCAPTCHA errors: ' . json_encode($recaptchaResponse['error-codes']), 'debug');
}
} catch (\Exception $e) {
$this->Flash->error(__('reCAPTCHA verification failed: ' . $e->getMessage()));
// Debug: Log exception
$this->log('Exception: ' . $e->getMessage(), 'debug');
}
} else {
// Debug: Log if the request is not a POST request
$this->log('Request is not a POST request', 'debug');
}
$this->set(compact('enquiry'));
}
Where I have "SITE-KEY" I have placed with the actual string of my site key and same with "PROJECT-ID"
Currently testing on localhost (added to domain) and when I fill out the form, then complete the captcha, in the console I get the error "Error: Invalid site key or not loaded in api.js" and the form does not save to the DB.
I am at my wits end and would greatly appreciate any help or guidance as to what I am doing wrong.
As if to rub salt in the wounds I must complete the exact captcha I am trying to implement when submitting this post ha