reCAPTCHA integration on ServiceNow on Portal Forms - User bypasses captcha

Community Alums
Not applicable

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

Usajid_0-1699970539132.png

Usajid_1-1699970573153.png

 

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;
			}
	}
})();

 



1 ACCEPTED SOLUTION

Community Alums
Not applicable

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.

 

usmansafk_1-1700560587333.png


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

$scope.page.g_form.setValue('captcha_verification', 'Yes'); is the name of the field in the portal form stored within the Variable Set. Keep this naming consistent. 

 

 

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:

usmansafk_2-1700563127026.png 

After the user presses confirm, the captcha disappears and user receives a message:

usmansafk_3-1700563226182.png

If the user attempts to ignore the captcha and submit the form, the user receives an error message:

usmansafk_4-1700563315918.png


_______________________________

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!

 

View solution in original post

9 REPLIES 9

From the embedded widget you can set variable values in the client controller with this script line: $scope.page.g_form.setValue('variable_name',[desired_value]);

Community Alums
Not applicable

@mads2 
I have tried doing so, but the Client controller script does not make any change to the portal..?
I tried created 3 field types on the portal and none of them were successful. Any ideas?

Usajid_0-1700236398445.png

Usajid_1-1700236452705.png

 

 

Community Alums
Not applicable

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.

 

usmansafk_1-1700560587333.png


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

$scope.page.g_form.setValue('captcha_verification', 'Yes'); is the name of the field in the portal form stored within the Variable Set. Keep this naming consistent. 

 

 

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:

usmansafk_2-1700563127026.png 

After the user presses confirm, the captcha disappears and user receives a message:

usmansafk_3-1700563226182.png

If the user attempts to ignore the captcha and submit the form, the user receives an error message:

usmansafk_4-1700563315918.png


_______________________________

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!

 

Hi @Community Alums , were you able to get the custom variable to show publicly? I could not set create role/read role as public. In fact, when custom variable is selected, the entire permission section is hidden.

Flavia Margaret
Tera Contributor

hi @Community Alums , do you know if this reCAPTCHA key will ever expire? If so, do you know what's the expiration frequency, e.g. It will expire after 3 months, etc?