- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎11-14-2023 04:28 AM - edited ‎11-21-2023 01:29 AM
The requirement:
My organization has a public Service Portal in which both users and external can submit form. If the user is not logged in and submits the form, they want to Implement reCAPTCHA on these public portal forms.
On my PDI I have successfully created a widget that uses Google reCAPTCHA and provided it with the site and secret key. This is now working as seen in the screenshot. I next created a custom variable on a random Catalog Item and linked my previously created widget.
Question:
Now on the portal form, although the reCAPTCHA is there - the user can simply ignore and bypass the captcha and submit the form. How can I ensure that this is made mandatory, and prevents form submit unless successfully completed?
Catpcha Widget Code:
Body HTML template
<script src='https://www.google.com/recaptcha/api.js'></script>
<body>
<div class="g-recaptcha" data-sitekey="{{data.sitekey}}"></div>
<input type="button" value="Confirm" ng-click="c.checkCap()" />
</body>
Client Controller:
function() {
/* widget controller */
var c = this;
/**
* c.checkCap should be called via ng-click, and will return a boolean value,
* indicating whether the user is indeed validated to not be some sort of mechanized
* button-pushing device.
* If the user has performed no such validation, then an alert is shown, admonishing
* them for attempting to take over the human race.
*/
c.checkCap = function() {
var resp;
c.data.captchaSession = grecaptcha.getResponse();
if (!c.data.captchaSession) {
alert('You have not passed reCaptcha verification. \nPlease try again.');
resp = false;
}
c.server.update().then(function() {
var captchaResponse = c.data.captchaResponse;
if (captchaResponse) {
resp = isThisTrueOrWhat(captchaResponse);
}
});
grecaptcha.reset();
//Here, you might perform some action to allow or disallow submission based on the boolean value of the resp variable.
return resp;
}
}
function isThisTrueOrWhat(b) {
return ((typeof b == 'string') ? (b.toLowerCase() == 'true') : (b == true)); //all this just to properly return a bool in JS. THERE'S GOT TO BE A BETTER WAY!
}
Server Script
(function() {
/* populate the 'data' object */
/* e.g., data.table = $sp.getValue('table'); */
data.sitekey = gs.getProperty('recaptcha.site-key');
if (input) {
data.captchaResponse = null;
data.captchaSession = input.captchaSession;
try {
var RESTCAPTCHA = new sn_ws.RESTMessageV2();
RESTCAPTCHA.setHttpMethod('post');
RESTCAPTCHA.setEndpoint('https://www.google.com/recaptcha/api/siteverify');
RESTCAPTCHA.setQueryParameter('secret', gs.getProperty('recaptcha.secret-key'));
RESTCAPTCHA.setQueryParameter('response', data.captchaSession);
var captchaResponse = RESTCAPTCHA.execute();
if (captchaResponse.haveError()) {
gs.logError('Error in validating captcha response: ' + captchaResponse.getErrorMessage() + '. Status code: ' + captchaResponse.getStatusCode(), 'CaptchaAjax script include');
data.captchaResponse = false;
}
var successResponse = JSON.parse(captchaResponse.getBody()).success; //Relies on ES5 syntax. For ES3, use new JSON().decode(json_string);
data.captchaResponse = successResponse;
} catch (ex) {
gs.logError('Error in processing response from reCAPTCHA: ' + ex.message, 'CaptchaAjax script include');
data.captchaResponse = false;
}
}
})();
Solved! Go to Solution.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎11-21-2023 02:45 AM - edited ‎11-21-2023 02:47 AM
Thanks for your help @mads2. However I managed to find the reason as to why the widget script was not updating the Catalog Item Form. It was actually an issue with my Widget Client Controller script not utilizing:
api.controller = function($scope) {}
For anyone else benefit in the future viewing this, with similar requirements; Implementing Google reCAPTCHA with ServiceNow Catalog Item Forms, below are the steps and code to follow:
1. Attain Google reCAPTCHA Site & Secret Key from: https://www.google.com/recaptcha/admin/create
The domains should correspond to each instance URL of your ServiceNow instance. So they will be a separate keys for dev | uat | live instances.
2. Insert the keys into as properties in the sys_properties table on ServiceNow
3. Create a Google reCAPTCHA widget in ServiceNow.
Within the client controller you will reference a boolean/ checkbox/ yes-no field that will update depending on if captcha is true.
Widget code:
Body HTML template
<script src='https://www.google.com/recaptcha/api.js'></script>
<body>
<div class="g-recaptcha" data-sitekey="{{data.sitekey}}"></div>
<input type="button" value="Confirm" ng-click="c.checkCap()" />
</body>
Server script
(function() {
data.sitekey = gs.getProperty('recaptcha.site-key');
if (input) {
data.captchaResponse = null;
data.captchaSession = input.captchaSession;
try {
var RESTCAPTCHA = new sn_ws.RESTMessageV2();
RESTCAPTCHA.setHttpMethod('post');
RESTCAPTCHA.setEndpoint('https://www.google.com/recaptcha/api/siteverify');
RESTCAPTCHA.setQueryParameter('secret', gs.getProperty('recaptcha.secret-key'));
RESTCAPTCHA.setQueryParameter('response', data.captchaSession);
var captchaResponse = RESTCAPTCHA.execute();
if (captchaResponse.haveError()) {
gs.logError('Error in validating captcha response: ' + captchaResponse.getErrorMessage() + '. Status code: ' + captchaResponse.getStatusCode(), 'CaptchaAjax script include');
data.captchaResponse = false;
}
var successResponse = JSON.parse(captchaResponse.getBody()).success;
data.captchaResponse = successResponse;
} catch (ex) {
gs.logError('Error in processing response from reCAPTCHA: ' + ex.message, 'CaptchaAjax script include');
data.captchaResponse = false;
}
}
})();
Client controller
api.controller = function($scope) {
/* widget controller */
var c = this;
/**
* c.checkCap should be called via ng-click, and will return a boolean value,
* indicating whether the user is validated and not a bot
* If the user has performed no such validation, then an alert is shown
*/
c.checkCap = function() {
var resp;
c.data.captchaSession = grecaptcha.getResponse();
if (!c.data.captchaSession) {
alert('You have not passed Captcha verification. \nPlease try again.');
resp = false;
}
c.server.update().then(function() {
var captchaResponse = c.data.captchaResponse;
if (captchaResponse) {
resp = confirmTrue(captchaResponse);
$scope.page.g_form.setValue('captcha_verification', 'Yes');
}
});
grecaptcha.reset();
return resp;
}
}
function confirmTrue(b) {
return ((typeof b == 'string') ? (b.toLowerCase() == 'true') : (b == true));
}
4. Create a Variable Set in which the Captcha will be stored.
Adding it to variable set will enable usability to any form in which Captcha is to be integrated.
Order: 10,000 (the make it generally at the bottom of most forms)
Variables:
line_break | Break Type field| (Only for cleanliness, as this way the captcha will be serrated from)
google_recaptcha | Custom with Label field | 'Please confirm the Captcha below to proceed.'
captcha_verification | Yes / No field | 'Captcha Verified?' (field will be hidden)
Catalog UI Polices:
a. Hide Google Captcha if Passed -
Simply, IF captcha_verification is YES, then HIDE google_recaptcha
Also, would suggest adding an info message in the Runs script section:
g_form.addInfoMessage('Thank you for confirming Captcha.');
b. Hide 'Captcha Verified' field -
No conditions, just simply hide captcha_verification always
Catalog Client Scripts:
a. Prevent form submission on Captcha fail -
onSubmit client script to prevent user by passing the captcha
function onSubmit() {
var captchaVerification = g_form.getValue('captcha_verification');
if (captchaVerification != 'Yes') {
g_form.addErrorMessage('You have not passed Captcha. Please try again');
g_form.submitted = false;
return false; //Abort the submission
}
}
Final product:
After the user presses confirm, the captcha disappears and user receives a message:
If the user attempts to ignore the captcha and submit the form, the user receives an error message:
_______________________________
That's all - I hope this helped. There was not much up to date resources in reCaptcha integration on ServiceNow community, YT etc., so i thought I would share this solution. Although this could certainly be refined, i.e. removal of the Confirm button (will leave you all with that one for yourselves 😉) - this certainly can serve as an MVP.
Please mark as helpful if this helped!
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎11-14-2023 03:41 PM
A simple solution might be to create a hidden checkmark variable that will be set to true by the widget when the REST API returns a successful response.
Then you can create a onSubmit client script that evaluates if the checkmark is true and abort submit if it is not true.
I don't know how to modify the variable value from the widget but I guess it is possible in some way.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎11-16-2023 01:26 PM
You can set a hidden variable using $scope.page.g_form.setValue('variable_name',true);
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎11-17-2023 04:03 AM
Ok will try this today and reply.
If successful will make a post on community with the complete solution and code. It seems there has been people wanting implement this; reCAPTCHA integration on public forms, however there is not a comprehensive article or resource for it.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎11-17-2023 01:55 AM
"A simple solution might be to create a hidden checkmark variable that will be set to true by the widget when the REST API returns a successful response."
How to reference the widget from a client script?