Altcha
An open-source alternative equivalent to reCaptcha.
Introduction
Implementing a captcha security mechanism is extremely important, here are some relevant reasons:
- To prevent bots from automatically accessing API addresses.
- To ensure that access is made by a real person.
- It also ensures that access was made through a browser.
For a long time, the captcha mechanism offered a challenge for the user to complete.
Nowadays this is unnecessary, because an encrypted key mechanism is used combined with a challenge that needs to be solved by an algorithm that uses browser interaction, thus detecting whether it is a human or robotic interaction.
This method is ingenious and reliable; with just one click it detects whether you are human or a bot.
Altcha is an open-source project that implements this security process, with a frontend widget that also integrates with React, and a backend resource offered by Netuno.
Limitations
Altcha uses the Web Crypto API and only works on secure HTTPS addresses with an SSL certificate, or locally.
On the frontend, if the domain is not
localhost, the URL must be a secure HTTPS URL with an SSL certificate.
When the frontend is accessed locally, browsers only allow the use of the Web Crypto API
at the local address, necessarily localhost, otherwise it requires secure HTTPS with an SSL certificate.
Backoffice
To enable authentication with Altcha in your Netuno application's back office, use the following configuration in the application:
{
...
"auth": {
"altcha": {
"admin": {
"enabled": true
}
}
},
...
}
Great, now just access the backoffice login screen and you'll see the Altcha checkbox fully functional.
How it Works
Netuno provides a standard service in its REST API that supplies the Altcha parameters to the frontend; typically, the address for this service is:
http://localhost:9000/services/_altcha
Using the parameters, the Altcha widget on the frontend generates the digitally signed
challenge result, which is sent to the backend in the hidden altcha field of the form.
On the backend, it is validated whether the result and the digital signature are indeed correct.
Widget in HTML
Example of how the widget is integrated into the backoffice login HTML:
<script type="text/javascript" src="/scripts/plugins/altcha/altcha.i18n.umd.min.cjs"></script>
<form ...>
<input id="inputAltcha" type="hidden" name="altcha" value="">
<altcha-widget id="widgetAltcha"
challenge="/services/_altcha" language="pt"
hideLogo hideFooter></altcha-widget>
<script>
document.getElementById("widgetAltcha").addEventListener("statechange", function (e) {
if (e.detail.state === "verified") {
document.getElementById("inputAltcha").value = e.detail.payload;
}
}, false);
</script>
...
</form>
Note that the URL used in the challenge attribute of the widget is the service automatically generated by
Netuno with the challenge parameters.
External Authentication
Authentication typically implemented and secured with JWT uses the standard REST API service for authentication, which is usually:
http://localhost:9000/services/_auth
This service, auto-generated by Netuno, ensures secure external authentication with native JWT, and also validates the captcha widget challenge result.
To enable Altcha for authentication, add the following configuration:
{
...
"auth": {
"altcha": {
"enabled": true
}
},
...
}
Therefore, only authentication requests, i.e., logins that send the key from the challenge result generated by Altcha and that is valid, will be able to authenticate successfully; otherwise, the login will be blocked.
The Altcha widget must be integrated into the frontend, and the challenge result key must be sent in the
altchafield.
Widget in React
See below how to integrate the widget in React:
Add the dependency for Altcha:
bun add altcha
Import Altcha into the code:
import "altcha/i18n";
Create the state to store the challenge result key:
const [altchaPayload, setAltchaPayload] = useState(null);
Create an internal reference in the React component to the widget:
const altcha = useRef(null);
Add the component loading effect that uses the widget reference, with an event that watches for when the widget's checkbox is clicked to load the challenge result key into the state:
useEffect(() => {
if (altcha && altcha.current) {
function altchaVerified(ev) {
if (ev.detail.state === "verified") {
setAltchaPayload(ev.detail.payload);
}
}
altcha.current.addEventListener("statechange", altchaVerified, false);
return () => {
if (altcha.current != null) {
altcha.current.removeEventListener("statechange", altchaVerified, false);
}
}
}
}, [altcha]);
Example of the widget in the React component's "HTML":
<altcha-widget
ref={altcha}
challenge={_service.url('/_altcha')}
language="pt"
delay={1}
hideLogo={true}
hideFooter={true}
></altcha-widget>
Auth Client
See how to integrate Altcha with Auth Client.
Use the code below in the event that sends the login form data to the backend, such as the onSubmit, onClick,
or onFinish events of Ant.Design forms:
_auth.login({
username,
password,
data: (data) => {
data.altcha = altchaPayload;
return data;
},
success: ({json}) => {
...
},
fail: (data) => {
...
}
});
Note that altchaPayload is the state that holds the key for the captcha challenge result.
Full example on the ReAuthKit Login page:
Integration in Services
In addition to authentication, as seen above, the captcha mechanism can be integrated with Altcha in any other type of service.
It is recommended to integrate captcha into any public service that may be the target of attacks, such as the exhaustive automation of bots for mass data logging.
On the frontend, the security widget from Altcha should also be implemented on the screen where the REST API service is integrated; that is, the widget should be used wherever the service is called.
Service Client
See below how to integrate Altcha with Service Client.
The integration of the widget in the frontend is similar to what was explained above:
We need to obtain the widget payload, exactly as exemplified above, following the same login authentication process.
The payload is sent to the backend via a URL parameter, a form field, or as a JSON property, depending on the HTTP method and how the service is implemented.
Example of how to send the payload to the registration service, or to create a new user account:
_service({
method: 'POST',
url: 'register',
data: {
name,
email,
password,
altcha: altchaPayload
},
success: (response) => {
...
},
fail: (e) => {
...
}
});
Note that altchaPayload is the state that holds the key for the captcha challenge result.
Full example on the ReAuthKit Registration page:
Backend
With the payload being sent to the REST API service, we need to validate it in the service's backend code. Here's how:
- JavaScript
- TypeScript
- Python
- Ruby
- Kotlin
- Groovy
const altchaPayload = _req.getString("altcha");
if (_auth.altchaEnabled() && !_altcha.verifySolution(altchaPayload)) {
_header.status(409);
_out.json(
_val.map()
.set("error", "invalid-altcha-payload")
);
_exec.stop();
}
const altchaPayload = _req.getString("altcha");
if (_auth.altchaEnabled() && !_altcha.verifySolution(altchaPayload)) {
_header.status(409);
_out.json(
_val.map()
.set("error", "invalid-altcha-payload")
);
_exec.stop();
}
altchaPayload = _req.getString("altcha")
if _auth.altchaEnabled() and _altcha.verifySolution(altchaPayload) == False:
_header.status(409)
_out.json(
_val.map()
.set("error", "invalid-altcha-payload")
)
_exec.stop()
altchaPayload = _req.getString("altcha")
if _auth.altchaEnabled() and _altcha.verifySolution(altchaPayload) == false
_header.status(409)
_out.json(
_val.map()
.set("error", "invalid-altcha-payload")
)
_exec.stop()
end
val altchaPayload = _req.getString("altcha")
if (_auth.altchaEnabled() && !_altcha.verifySolution(altchaPayload)) {
_header.status(409)
_out.json(
_val.map()
.set("error", "invalid-altcha-payload")
)
_exec.stop()
}
final altchaPayload = _req.getString("altcha")
if (_auth.altchaEnabled() && !_altcha.verifySolution(altchaPayload)) {
_header.status(409)
_out.json(
_val.map()
.set("error", "invalid-altcha-payload")
)
_exec.stop()
}
Complete example of the ReAuthKit Registration service:
Learn more about low-code and polyglot programming features:
Conclusion
In a very simplified way, we can create a security mechanism in the API services to prevent bots and ensure human interaction in the browser.
It is recommended to use the captcha mechanism with Altcha in the most sensitive public services, such as registration and authentication services.
With the native implementation that Netuno offers, it becomes very simple to activate and use this mechanism in any REST API service.
It is recommended that the self-generated Netuno backoffice always have authentication with Altcha enabled in production, and with HTTPS and an SSL certificate.
Whenever possible, use Altcha to ensure the security and robustness of your API's public services.