This happens to be my very first Cloudm@tters post. The title shouldn’t be too misleading, it’s all about Google’s Recaptcha and Salesforce Lightning. More specifically Lighting Communities. The Salesforce Winter ’18 release brought us “Communities in Enterprise, Performance, and Unlimited Editions – Even Without Communities Licenses“. A great benefit, because any customer on an Enterprise, Performance or Unlimited plan can leverage a public facing community, included in their licenses. With that remark, that page views are limited to 500,000 for Enterprise and 1,000,000 for Unlimited. And you get the full-featured capabilities of Lightning Templates and Community Builder.
While working on a VTO engagement, to deliver a pretty straight-forward landing page for fundraising – I opted for this new Winter ’18 feature. The non-profit is benefitting through their Salesforce.org membership, from this feature because through Salesforce.org an eligible non-profit owns 10 Sales Cloud Enterprise Edition licenses.
I was looking for something like below, where using a public community we’d protect using Recaptcha the first step of creating a donation, that is providing a set of credentials. Clicking the Recaptcha would – if successful – pass this first step.

After successfully passing the Recaptcha check…

Creating a Lightning Customer Service Napili-based community these days is a matter of just a few clicks. Which is great, because you get a descent looking landing page without any hassle. And instead of having to deal with traditional Web-to-lead once could immediately create the record in a Salesforce object. But being a public and open-to-all landing page, the intrinsic risk of bot abuse is apparent. To safeguard for that, Recaptcha is the default tool of choice these days.
Recaptcha provides a really simple API. But that same API has one very big caveat. It’s a dynamic library, hence cannot be hosted as static resource on the Salesforce platform. If you’d load the library, this is what you’d get. And you’ll be immediately informed about the correct use…
/* PLEASE DO NOT COPY AND PASTE THIS CODE. */
(function () {
if (!window['___grecaptcha_cfg']) {
window['___grecaptcha_cfg'] = {};
};
if (!window['___grecaptcha_cfg']['render']) {
window['___grecaptcha_cfg']['render'] = 'onload';
};
window['__google_recaptcha_client'] = true;
var po = document.createElement('script');
po.type = 'text/javascript';
po.async = true;
po.src = 'https://www.gstatic.com/recaptcha/api2/r20171115120512/recaptcha__nl.js';
var elem = document.querySelector('script[nonce]');
var nonce = elem && (elem['nonce'] || elem.getAttribute('nonce'));
if (nonce) {
po.setAttribute('nonce', nonce);
}
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(po, s);
})();
Because the library is dynamic, and it cannot serve as a static resource – using the library within a Lightning Component isn’t possible. At least not through the traditional route to embed 3rd-party libraries.
However, embedding Recaptcha within a Visualforce page, is a more traditional means. Has been done often enough, and it’s documented. A Visualforce page can be easily exposes as part of a Lightning component by means of including an iframe source. Not ideal, by any means because it breaks the default Lightning app & component communication pattern. Default you’d either use composition or lightning events. But working with an iframe, you’re bound to using more old-school (or should I say traditional) means: using postMessage. Salesforce has a quality reading on this topic, so I don’t need to write it again. The essence is that an iframe owns it own DOM, as it where a browser-in-a-browser.
The good thing of Visualforce pages is, that is allows embedding in a more traditional way 3rd-party libraries. And that method has been proven for many years. So let’s start with that Visualforce page then.
<apex:page controller="reCAPTCHA">
<!-- Styling to eliminate border on the iFramed content -->
<style type="text/css">
.bPageBlock.brandSecondaryBrd.apexDefaultPageBlock.secondaryPalette {
border: none;
}
</style>
<apex:pageBlock >
<apex:form >
<!-- recaptcha placeholder element -->
<div id='recaptcha'/>
<script type="text/javascript">
var captchaSuccess = function() {
var origin = location.origin;
parent.postMessage('success', origin);
};
var repatchaReady = function() {
grecaptcha.render('recaptcha', {
'sitekey' : '{!publicKey}'
, 'callback': captchaSuccess
});
};
</script>
<!-- The Google reCaptcha API loads first with a callback -->
<script src="{!apiEndpoint}?onload=repatchaReady&render=explicit"/>
</apex:form>
</apex:pageBlock>
</apex:page>
Loading the Recaptcha library following Google’s advice, provides a callback. You only nee to pass in the ‘site key’ which you get after registering with Google (which is a completely free service, I forgot to mention). The code snippet should be pretty much self-explanatory. Beware that I use custom settings, to cater for a generic and re-usable solution. And the piece of embedded CSS (typically a no-go) is simply to get rid of the colored like around the iframe (that colored line is nasty, and cannot be suppressed differently I found). Oh yeah, thing to mention about XSS. To cater for same-origin, make sure you pass in the origin when posting to the parent.
The controller is simple, nothing fancy. Just getting the necessary custom settings.
public class reCAPTCHA {
public static String apiEndpoint {
get { return ( getCustomSetting('ApiEndpoint__c')); }
}
public static String publicKey {
get { return ( getCustomSetting('PublicKey__c')); }
}
private static String getCustomSetting(String customSetting) {
try {
return ( String.valueof( GoogleRecaptcha__c.getValues('Recaptcha').get(customSetting) ) );
}
catch (Exception e) {
system.debug('[Recaptcha Exception] it seems the Recaptcha Custom Settings are not available:' + e.getMessage());
return ('NOK');
}
}
}<span id="mce_SELREST_start" style="overflow:hidden;line-height:0;"></span>
The Visualforce page itself should be made public as well, so it will live in the same domain as the community. That actually tackles the same-origin problem already. To make your Visualforce page publicly accessible, you need to go through a number of steps:
- Setup > All Communities > Workspaces (wait to have it load)
- Click on the “Communities Workspaces” tile in the top left-hand corner
- Click “Administration” then click on “Go to Force.com”

- You’ll see that “Clickjack Protection Level” is set to “Allow framing by the same origin only”. On the top click on “Public Access Settings”, then click “Visualforce Page Access”
- Click “Edit” to add the Visualforce page hosting the Recaptcha libary, and save.
That done, it’s time to scaffold the Lightning component itself. Since goal is to create a reusable component (remember, Lightning is all about Composition) I start of with a the component which just hold the Visualforce page and handles the communication.
The component’s controller (yes, it ain’t much):
({
init: function(component, event) {
window.addEventListener("message", function(event) {
component.getEvent("reCaptchaSuccessEvt").fire();
}, false);
}
})
Really? Yep. Really. To communicate with the component’s parent, an event is fired of “reCaptchaSuccessEvt”. That event may or may not be handled upstream. That’s the beauty of loose coupled events.
Now, the Lightning component which takes it’s hasn’t a lot to do.
<aura:component implements="forceCommunity:availableForAllPageTypes" controller="KCS_LoginController">
<aura:attribute name="loginResult" type="Boolean" default="false"/>
<aura:attribute name="initialLogin" type="Boolean" default="true"/>
<aura:attribute name="emailProfile" type="String"/>
<aura:attribute name="nameProfile" type="String"/>
<aura:attribute name="urlReCaptchaVF" type="string"/>
<aura:handler name="init" value="{!this}" action="{!c.init}"/>
<!-- registering event handler -->
<aura:handler name="reCaptchaSuccessEvt" event="c:reCaptchaSuccessEvt" action="{!c.handleLogin}"/>
<aura:if isTrue="{!!v.loginResult}">
<lightning:card title="Inloggen">
<!-- <aura:set attribute="actions">
<lightning:button variant="brand" label="Inloggen" onclick="{!c.handleLogin}"/>
</aura:set> -->
<lightning:layout >
<lightning:layoutItem padding="around-small">
<lightning:input aura:id="email" variant="brand" type="email" label="Email" name="email" placeholder="Vul hier jouw emailadres in" />
</lightning:layoutItem>
<lightning:layoutItem padding="around-small">
<lightning:input aura:id="pin" type="password" label="Pincode" name="pin" placeholder="5 cijfers van jouw pincode" minLength="5" maxLength="5"/>
</lightning:layoutItem>
</lightning:layout>
<!-- passing in both the Visual force page hosting the reCaptcha and the originating host url -->
<c:reCaptchaLightning urlReCaptchaVF="{!v.urlReCaptchaVF}"/>
</lightning:card>
</aura:if>
<aura:if isTrue="{!v.loginResult}">
<lightning:tile label="Welkom!">
<aura:set attribute="media">
<lightning:avatar src="https://pbs.twimg.com/media/CtUdvL3UEAAZZrp.jpg" class="slds-avatar_circle slds-avatar_large" alternativeText="Astro"/>
</aura:set>
<dl class="slds-dl--horizontal">
<dt class="slds-dl--horizontal__label">
<p class="slds-truncate" title="Company">Ik ben:</p>
</dt>
<dd class="slds-dl--horizontal__detail slds-tile__meta">
<p class="slds-truncate" title="Salesforce">{!v.nameProfile}</p>
</dd>
<dt class="slds-dl--horizontal__label">
<p class="slds-truncate" title="Email">Email:</p>
</dt>
<dd class="slds-dl--horizontal__detail slds-tile__meta">
<p class="slds-truncate" title="salesforce-ux@salesforce.com">{!v.emailProfile}</p>
</dd>
</dl>
</lightning:tile>
</aura:if>
</aura:component>
And a few lines of support Apex controller code, to get the site prefix.
public class KCS_LoginController {
@AuraEnabled
public static String getCommunityUrlPrefix () {
return [SELECT UrlPathPrefix FROM Network WHERE Id = :Network.getNetworkId()].UrlPathPrefix;
}
}
Create the few neccesary custom settings, don’t forget about that. Plugged all together, and you have a beautiful Lightning-enabled safeguard for bots. Enjoy!
PS: Will put stuff on Git soon.