├── main └── default │ ├── lwc │ └── myLWC │ │ ├── myLWC.html │ │ └── myLWC.js │ ├── staticresources │ ├── reCAPTCHAv3.resource-meta.xml │ └── reCAPTCHAv3.html │ └── classes │ └── reCAPTCHAv3ServerController.cls └── README.md /main/default/lwc/myLWC/myLWC.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main/default/staticresources/reCAPTCHAv3.resource-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Public 4 | text/html 5 | -------------------------------------------------------------------------------- /main/default/classes/reCAPTCHAv3ServerController.cls: -------------------------------------------------------------------------------- 1 | public with sharing class reCAPTCHAv3ServerController { 2 | public reCAPTCHAv3ServerController(){ 3 | 4 | } 5 | 6 | 7 | @AuraEnabled 8 | public static Boolean isReCAPTCHAValid(String tokenFromClient) { 9 | String SECRET_KEY = 'reCAPTCHA_secret_key'; 10 | String RECAPTCHA_SERVICE_URL = 'https://www.google.com/recaptcha/api/siteverify'; 11 | Http http = new Http(); 12 | 13 | HttpRequest request = new HttpRequest(); 14 | 15 | request.setEndpoint(RECAPTCHA_SERVICE_URL + '?secret=' + SECRET_KEY + '&response' + tokenFromClient); 16 | request.setMethod('POST'); 17 | request.setHeader('Content-Length', '0'); 18 | HttpResponse response = http.send(request); 19 | 20 | Map mapOfBody = (Map) JSON.deserializeUntyped(response.getBody()); 21 | 22 | Boolean success = (Boolean) mapOfBody.get('success'); 23 | 24 | return success; 25 | } 26 | } -------------------------------------------------------------------------------- /main/default/staticresources/reCAPTCHAv3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | reCAPTCHA html resource 4 | 5 | 6 | 7 | 8 | 21 | 22 | -------------------------------------------------------------------------------- /main/default/lwc/myLWC/myLWC.js: -------------------------------------------------------------------------------- 1 | import { LightningElement, track, api } from 'lwc'; 2 | import pageUrl from '@salesforce/resourceUrl/reCAPTCHAv3'; 3 | import isReCAPTCHAValid from '@salesforce/apex/reCAPTCHAv3ServerController.isReCAPTCHAValid'; 4 | 5 | export default class GoogleCapatcha extends LightningElement { 6 | @api formToken; 7 | @api validReCAPTCHA = false; 8 | 9 | @track navigateTo; 10 | captchaWindow = null; 11 | 12 | constructor(){ 13 | super(); 14 | this.navigateTo = pageUrl; 15 | } 16 | 17 | captchaLoaded(evt){ 18 | var e = evt; 19 | console.log(e.target.getAttribute('src') + ' loaded'); 20 | if(e.target.getAttribute('src') == pageUrl){ 21 | 22 | window.addEventListener("message", function(e) { 23 | if (e.data.action == "getCAPCAH" && e.data.callCAPTCHAResponse == "NOK"){ 24 | console.log("Token not obtained!") 25 | } else if (e.data.action == "getCAPCAH" ) { 26 | this.formToken = e.data.callCAPTCHAResponse; 27 | isReCAPTCHAValid({tokenFromClient: formToken}).then(data => { 28 | this.validReCAPTCHA = data; 29 | }); 30 | } 31 | }, false); 32 | } 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # How to use Google reCAPTCHA v3 in Lightning Web Component 2 | 3 | ## Limitation of Salesforce platform 4 | Salesforce platform puts restriction on loading JS source into Lightning Web Component directly, i.e. the magic reCAPTCHA script cannot be directly used in the component. 5 | 6 | 7 | > "You can’t load JavaScript resources from a third-party site, even a CSP Trusted Site. To use a JavaScript library from a third-party site, add it to a static resource, and then add the static resource to your component. After the library is loaded from the static resource, you can use it as normal." - [Call APIs from JavaScript](https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.js_api_calls) 8 | 9 | There are 4 steps to integrate reCAPTCHA v3 to a Lightning Web Component 10 | ### 1. Create html static resource with reCAPTCHA 11 | reCAPTCHAv3.html 12 | ```html 13 | 14 | 15 | reCAPTCHA html resource 16 | 17 | 18 | 19 | 20 | 33 | 34 | 35 | ``` 36 | reCAPTCHAv3.resource-meta.xml 37 | ```xml 38 | 39 | 40 | Public 41 | text/html 42 | 43 | ``` 44 | 45 | ### 2. Create Lightning Web Component with an iframe to load the static resource 46 | myLWC.html 47 | ```html 48 | 51 | ``` 52 | 53 | ### 3. Create the Javascript controller for the Lightning Web Component 54 | myLWC.js 55 | ```javascript 56 | import { LightningElement, track, api } from 'lwc'; 57 | import pageUrl from '@salesforce/resourceUrl/reCAPTCHAv3'; 58 | import isReCAPTCHAValid from '@salesforce/apex/reCAPTCHAv3ServerController.isReCAPTCHAValid'; 59 | 60 | export default class GoogleCapatcha extends LightningElement { 61 | @api formToken; 62 | @api validReCAPTCHA = false; 63 | 64 | @track navigateTo; 65 | captchaWindow = null; 66 | 67 | constructor(){ 68 | super(); 69 | this.navigateTo = pageUrl; 70 | } 71 | 72 | captchaLoaded(evt){ 73 | var e = evt; 74 | console.log(e.target.getAttribute('src') + ' loaded'); 75 | if(e.target.getAttribute('src') == pageUrl){ 76 | 77 | window.addEventListener("message", function(e) { 78 | if (e.data.action == "getCAPCAH" && e.data.callCAPTCHAResponse == "NOK"){ 79 | console.log("Token not obtained!") 80 | } else if (e.data.action == "getCAPCAH" ) { 81 | this.formToken = e.data.callCAPTCHAResponse; 82 | isReCAPTCHAValid({tokenFromClient: formToken}).then(data => { 83 | this.validReCAPTCHA = data; 84 | }); 85 | } 86 | }, false); 87 | } 88 | } 89 | 90 | } 91 | ``` 92 | 93 | 94 | ### 4. Create Apex class to handle the server-side verification 95 | reCAPTCHAv3ServerController.cls 96 | ```java 97 | public with sharing class reCAPTCHAv3ServerController { 98 | public reCAPTCHAv3ServerController(){ 99 | 100 | } 101 | 102 | 103 | @AuraEnabled 104 | public static Boolean isReCAPTCHAValid(String tokenFromClient) { 105 | String SECRET_KEY = 'reCAPTCHA_secret_key'; 106 | String RECAPTCHA_SERVICE_URL = 'https://www.google.com/recaptcha/api/siteverify'; 107 | Http http = new Http(); 108 | 109 | HttpRequest request = new HttpRequest(); 110 | 111 | request.setEndpoint(RECAPTCHA_SERVICE_URL + '?secret=' + SECRET_KEY + '&response' + tokenFromClient); 112 | request.setMethod('POST'); 113 | request.setHeader('Content-Length', '0'); 114 | HttpResponse response = http.send(request); 115 | 116 | Map mapOfBody = (Map) JSON.deserializeUntyped(response.getBody()); 117 | 118 | Boolean success = (Boolean) mapOfBody.get('success'); 119 | 120 | return success; 121 | } 122 | } 123 | ``` 124 | --------------------------------------------------------------------------------