Logotype Sitevision Developer
Log in
Log in

captcha example usage

The security.captcha SDK is pretty straightforward to use, but there are some considerations you need to keep in mind when implementing it.

How are you rendering the WebApp and the captcha widget?

If you use server or client rendering some different approaches apply. A simple server rendered for is very simple. If the captcha is added to the dom later on on the client, a couple of more steps are required.

Server rendered form

This example assumes a standard React rendering approach, render the compontent server side, and hydrate on the client. Since the captcha is immedietly loaded into the dom. Initiaton of the captcha widget will be managed by default by sitevision.

Example below shows an example with an intecepted request, and sending it with a call through requester. If you want to use a <form action="... approach instead, this is of course also available.

server rendered widget, example, index.js
js
import * as React from "react"; import { renderToString } from "react-dom/server"; import router from "@sitevision/api/common/router"; import security from "@sitevision/api/common/security"; import App from "./components/App"; router.get("/", (req, res) => { // Grab the HTML for the widget const captcha = security.captcha.render(); res.agnosticRender(renderToString(<App captcha={captcha} />), { captcha, }); }); router.post("/submit", (req, res) => { // verify() will verify the included captcha response // Which should be included from the data if (!security.captcha.verify()) { return res.status(400); } res.status(204); });
server rendered widget, example, main.js
js
import * as React from "react"; import { hydrateRoot } from "react-dom/client"; import App from "./components/App"; export default function main(initialState, el) { hydrateRoot(el, <App {...initialState} />); }
server rendered widget, example, App.js
js
import requester from "@sitevision/api/client/requester"; import router from "@sitevision/api/common/router"; import * as React from "react"; const App = ({ captcha }) => { // Add the captcha widget to the form and handle form submission return ( <form onSubmit={handleSubmit}> <div dangerouslySetInnerHTML={{ __html: captcha }} /> <input type="text" name="text" /> <input type="submit" value="Submit" /> </form> ); }; export default App; function handleSubmit(e) { e.preventDefault(); // Grab all data from the form. // This ensures that the captcha response is included in the data sent to the server. const data = Object.fromEntries(new FormData(e.target).entries()); requester .doPost({ url: router.getStandaloneUrl("/submit"), data, }) .then(() => { alert("Captcha Passed! Form submitted successfully."); }) .catch(() => { alert("Captcha Failed! Please try again."); }); }

Client rendered form

For a client rendered form we have a couple of minor things to take into account. Since the captcha widget will be inserted into the DOM after the page is loaded, we need to ensure the widgets are initiated ourselves. At the moment we support Friendly Captcha and Google reCAPTCHA, to ensure both work with your solution we need to make a couple of adjustments to initiate these.

The Google reCAPTCHA widgets contains script elements which needs to run. The Friendly Captcha needs to be initiated by a function call. You do not need to know which one is used in the WebApp, it's completely fine to handle both approaches to make a general and dynamic solution.

Here's an example:

client rendered widget, example, index.js
js
import router from "@sitevision/api/common/router"; import security from "@sitevision/api/common/security"; import App from "./components/App"; router.get("/", (req, res) => { // Grab the HTML for the widget const captcha = security.captcha.render(); res.agnosticRender('', { captcha }); }); router.post("/submit", (req, res) => { // verify() will verify the included captcha response // Which should be included from the data if (!security.captcha.verify()) { return res.status(400); } res.status(204); });
client rendered widget, example, main.js
js
import * as React from 'react'; import { createRoot } from 'react-dom'; import App from './components/App'; export default (initialState, el) => { createRoot(el).render(<App {...initialState} />); };
client rendered widget, example, App.js
js
import DangerouslySetHtmlContent from "dangerously-set-html-content"; import * as React from "react"; import security from "@sitevision/api/common/security"; import requester from "@sitevision/api/client/requester"; import router from "@sitevision/api/common/router"; const App = ({ captcha }) => { React.useEffect(() => { // Initialize the captcha widget after the component is mounted. security.captcha.init(); }, []); // The captcha widget might contain scripts that need to be executed after the component is mounted. // In this exampele we utilize the dangerously-set-html-content package to ensure that the scripts are executed. // The standard React approach of using dangerouslySetInnerHTML does not execute scripts, which is why we use this package instead. return ( <form onSubmit={handleSubmit}> <DangerouslySetHtmlContent className="env-m-bottom--small" html={captcha} /> <input type="text" name="text" /> <input type="submit" value="Submit" /> </form> ); }; export default App; function handleSubmit(e) { e.preventDefault(); // Grab all data from the form. // This ensures that the captcha response is included in the data sent to the server. const data = Object.fromEntries(new FormData(e.target).entries()); requester .doPost({ url: router.getStandaloneUrl("/submit"), data, }) .then(() => { alert("Captcha Passed! Form submitted successfully."); }) .catch(() => { alert("Captcha Failed! Please try again."); }); }

Manage captcha status

Above examples simply submits the form on a user action wether the captcha is solved or not. It is however possible to manage the user interface in the WebApp based on the current captcha status. For example, enable submit when the captcha is solved, or display a special error message on an error. To manage this you can either subscribe to an event, or manually check the status with a function call.

svCaptcha:stateChanged event

To get live status updates for captcha widgets. Subscribe to the svCaptcha:stateChanged event utilizsing the events.on SDK.

svCaptcha:stateChanged event example
js
import DangerouslySetHtmlContent from "dangerously-set-html-content"; import * as React from "react"; import security from "@sitevision/api/common/security"; import requester from "@sitevision/api/client/requester"; import router from "@sitevision/api/common/router"; import events from "@sitevision/api/common/events"; const App = ({ captcha }) => { const formRef = React.useRef(null); const [captchaValid, setCaptchaValid] = React.useState(false); React.useEffect(() => { security.captcha.init(); }, []); React.useEffect(() => { const handleCaptchaStateChanged = (status) => { const form = formRef.current; // The svCaptcha:stateChanged event is global for all captcha widgets on the page. // Check if the event is for the captcha widget in this form before updating the state. // The status object contains both the widget element and its id, use whichever fits your needs best. if (form && status?.widget && form.contains(status.widget)) { // Enable or disable the submit button based on the captcha validity state. setCaptchaValid(status.valid); // Exact state status are available on the status.state property, if a simple boolean is not sufficient. } }; // Subscribe to the captcha state change event on mount events.on("svCaptcha:stateChanged", handleCaptchaStateChanged); // Unsubscribe from the event on unmount return () => events.off("svCaptcha:stateChanged", handleCaptchaStateChanged); }, []); return ( //Add a ref to the form, used to localize the captcha widget on state change events <form ref={formRef} onSubmit={handleSubmit}> <DangerouslySetHtmlContent className="env-m-bottom--small" html={captcha} /> <input type="text" name="text" /> <input type="submit" value="Submit" disabled={!captchaValid} // Disable submit until captcha is valid /> </form> ); };

security.captcha.getState(container)

If you prefer to manually check the captcha state instead of utilising the event. The getState function is also available. Both the event and the function exposes the same data.

security.captcha.getState(container) example
js
function handleSubmit(e) { e.preventDefault(); const form = e.target; // To fetch the current state of the captcha widget we need to locate it. // getState requires an identifer or an element in which the captcha widget is rendered. const captchaState = security.captcha.getState(form); if (!captchaState.valid) { // If not valid, block the form submission. alert("Solve the captcha before submitting the form!"); return; } ...
Did you find the content on this page useful?