Monday, November 27, 2023

How to use Google reCAPTCHA v3 in Lightning Web Component

We have many applications that do require some authentication to prevent the attack vectors. To stop flooding the application with unnecessary attacks(eg. Robots running the scripts), we have to adopt some sort of authentication mechanism to regularize the application access. Google reCAPTCHA can help in identifying if the requests are coming from human or not


Google Configuration

  1. Go to https://www.google.com/recaptcha
  2. Click on the ‘Admin console’ button
  3. Click the ‘+’ create icon if you already have sites configured
  4. Enter a Label and select reCAPTCHA v3, v2 Checkbox or v2 Invisible.
  5. Add your custom or force.com community domain. (You can also add additional domains so that it will function in Experience Builder)
  6. Accept the terms of service and click the Submit button

Screen Shot 2020-04-19 at 3.24.06 PM.png
You will need to use your resulting keys in the code examples below. The keys are only valid for the type of reCAPTCHA selected during creation. If you want to test all 3 examples below, you must create each type in the admin console.
Screen Shot 2020-04-18 at 12.47.50 PM.png Site Key - This public key is intended to be exposed on your site and should be pasted directly in the JavaScript Head Markup examples (replace text ‘reCAPTCHA_site_key’).

Secret Key - This private key should live on the server-side only and be stored in a Custom Setting, Custom Metadata Type or Apex class (replace text ‘reCAPTCHA_secret_key’).

 

Experience Builder Settings

You will see Content Security Policy (CSP) errors as you implement the reCAPTCHA code examples. In Experience Builder → Settings → Security, add the trusted sites shown below and click the ‘Whitelist’ button as items show in the ‘CSP Errors’ list.
Screen Shot 2020-04-18 at 2.16.33 PM.png
 

There are 4 steps to integrate reCAPTCHA v3 to a Lightning Web Component

1. Create html static resource with reCAPTCHA

reCAPTCHAv3.html

<html>
    <head>
        <title></title>reCAPTCHA html resource</title>
        <script src="https://www.google.com/recaptcha/api.js?render=reCAPTCHA_site_key"></script>
    </head>
    <body>
        <input type="hidden" name="recaptcha_response" id="recaptchaResponse"/>
        <script type="text/javascript">
            grecaptcha.ready(function() {
                var reCAPTCHA_site_key = "reCAPTCHA_site_key";
                grecaptcha.execute('reCAPTCHA_site_key', {action: 'submit'}).then(function(token) {
                    recaptchaResponse.value = token;
                    if (token == "") {
                        parent.postMessage({ action: "getCAPCAH", callCAPTCHAResponse : "NOK"}, "*");
                    } else {
                        parent.postMessage({ action: "getCAPCAH", callCAPTCHAResponse : token}, "*");
                    }
                });
            }
        </script>
    </body>
</html>

reCAPTCHAv3.resource-meta.xml

<?xml version="1.0" encoding="UTF-8"?>
<StaticResource xmlns="http://soap.sforce.com/2006/04/metadata">
    <cacheControl>Public</cacheControl>
    <contentType>text/html</contentType>
</StaticResource>

2. Create Lightning Web Component with an iframe to load the static resource

myLWC.html

<template>
    <iframe src={navigateTo} name="captchaFrame" onload={captchaLoaded}></iframe>
</template>

3. Create the Javascript controller for the Lightning Web Component

myLWC.js

import { LightningElement, track, api } from 'lwc';
import pageUrl from '@salesforce/resourceUrl/reCAPTCHAv3';
import isReCAPTCHAValid from '@salesforce/apex/reCAPTCHAv3ServerController.isReCAPTCHAValid';

export default class GoogleCapatcha extends LightningElement {
    @api formToken;
    @api validReCAPTCHA = false;

    @track navigateTo;
    captchaWindow = null;

    constructor(){
        super();
        this.navigateTo = pageUrl;
    }

    captchaLoaded(evt){
        var e = evt;
        console.log(e.target.getAttribute('src') + ' loaded');
        if(e.target.getAttribute('src') == pageUrl){

            window.addEventListener("message", function(e) {
                if (e.data.action == "getCAPCAH" && e.data.callCAPTCHAResponse == "NOK"){
                    console.log("Token not obtained!")
                } else if (e.data.action == "getCAPCAH" ) {
                    this.formToken = e.data.callCAPTCHAResponse;
                    isReCAPTCHAValid({tokenFromClient: formToken}).then(data => {
                        this.validReCAPTCHA = data;
                    });
                }
            }, false);
        } 
    }

}

4. Create Apex class to handle the server-side verification

reCAPTCHAv3ServerController.cls

public with sharing class reCAPTCHAv3ServerController {
    public reCAPTCHAv3ServerController(){

    }


    @AuraEnabled
    public static Boolean isReCAPTCHAValid(String tokenFromClient) {
        String SECRET_KEY = 'reCAPTCHA_secret_key';
        String RECAPTCHA_SERVICE_URL = 'https://www.google.com/recaptcha/api/siteverify';
        Http http = new Http();

        HttpRequest request = new HttpRequest();

        request.setEndpoint(RECAPTCHA_SERVICE_URL + '?secret=' + SECRET_KEY + '&response' + tokenFromClient);
        request.setMethod('POST');
        request.setHeader('Content-Length', '0');
        HttpResponse response = http.send(request);

        Map<String, Object> mapOfBody = (Map<String, Object>) JSON.deserializeUntyped(response.getBody());

        Boolean success = (Boolean) mapOfBody.get('success');

        return success;
    }
}

Wednesday, November 22, 2023

How to Use LWC in Screen Flows

 To use a Lightning Web Component (LWC) in a Screen Flow in Salesforce, you'll need to follow these steps:

  1. 1)Create a Lightning Web Component:

    • Create a new Lightning Web Component or use an existing one. Ensure that your Lightning Web Component is properly configured and has the necessary properties and methods.
  2. 2)Expose Properties in the Lightning Web Component:

    • If your Lightning Web Component needs to receive input from the Flow, expose properties in your component's JavaScript file using the @api decorator.
    import { LightningElement, api } from 'lwc'; export default class MyLWC extends LightningElement { @api inputProperty; // Add more properties as needed }

  3. 3)Implement a Public Method (Optional):

    • If your Lightning Web Component needs to interact with the Flow or perform specific actions, you can expose a public method using the @api decorator.
    import { LightningElement, api } from 'lwc'; export default class MyLWC extends LightningElement { @api myPublicMethod() { // Your implementation here } }

  4. 4)Deploy the Lightning Web Component:

    • Deploy your Lightning Web Component to your Salesforce environment.
  5. 5)Create a Screen Flow:

    • In Salesforce Setup, navigate to "Flows" and create a new Screen Flow or edit an existing one.
  6. 6)Add a Lightning Web Component to the Screen Flow:

    • Drag the "Lightning Web Component" screen component onto your Flow canvas.
  7. 7)Configure the Lightning Web Component:

    • Select the Lightning Web Component screen component on the canvas.
    • In the right-hand properties pane, set the component name to your Lightning Web Component's name.
    • Configure any input properties by mapping them to Flow variables or values.
  8. 8)Run the Flow:

    • Save and activate your Flow.
    • Run the Flow by navigating to the record page or by using the Flow URL.

Now, your Lightning Web Component is embedded within the Screen Flow. The Flow can pass input values to the Lightning Web Component, and the Lightning Web Component can interact with the Flow if you exposed public methods.

Remember to handle any necessary logic and error scenarios in your Lightning Web Component based on the requirements of your Flow. Additionally, be aware of any governor limits that may apply when using Flows and Lightning Web Components in combination.