Altcha
An open-source alternative equivalent to reCaptcha.
Introduction
Implementing the captcha security mechanism is extremely important, for a few relevant reasons:
- Prevent robots from making automatic accesses to API addresses.
- Ensure that access is made by a real person.
- It also ensures that access was made with a browser.
For a long time, the captcha mechanism offered a challenge for the user to complete.
Nowadays this is unnecessary, because an encrypted keys mechanism is used combined with a challenge that must be solved by an algorithm that uses browser interaction, thus detecting whether the interaction is human or automated.
This method is ingenious and reliable; with just one click it detects whether it is a human or a robot.
Altcha is an open-source project that implements this security process, with a frontend widget that also integrates with React, and a backend feature that Netuno provides.
Limitations
Altcha uses the Web Crypto API, it only works on secure HTTPS addresses with an SSL certificate, or locally.
In the frontend, if the domain is not
localhost, the URL must necessarily be secure HTTPS with an SSL certificate.
When the frontend is accessed locally, browsers allow using the Web Crypto API
only on the local address, necessarily localhost; otherwise, secure HTTPS with
an SSL certificate is required.
Backoffice
To enable authentication with Altcha in the backoffice of your Netuno application, use the following configuration in the application:
{
...
"auth": {
"altcha": {
"admin": {
"enabled": true
}
}
},
...
}
Done, now just access the backoffice login screen and you will see the Altcha checkbox fully working.
How It Works
Netuno provides a standard service in the REST API that supplies the Altcha parameters to the frontend; normally the address of this service is:
http://localhost:9000/services/_altcha
With the parameters, the Altcha widget in the frontend generates the digitally signed challenge result, which
is sent to the backend in the form's hidden altcha field.
In the backend, it is validated whether the result and digital signature are really 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="en"
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 widget's challenge attribute is the service auto-generated by Netuno with the
challenge parameterization.
External Authentication
Authentication, normally 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 allows ensuring security for external authentication with native JWT, but it also validates the challenge result from the captcha widget.
To enable Altcha in authentication, add the configuration:
{
...
"auth": {
"altcha": {
"enabled": true
}
},
...
}
Thus, only authentication requests, that is, logins that send the challenge result key 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 Altcha dependency:
bun add altcha
Import Altcha in 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 for the widget:
const altcha = useRef(null);
Add the component loading effect that uses the widget reference, with an event that watches when the widget 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="en"
delay={1}
hideLogo={true}
hideFooter={true}
></altcha-widget>
Auth Client
See how to integrate Altcha into the Auth Client.
Use the code below in the event that sends the login form data to the backend,
such as the onSubmit, onClick events, or the onFinish event 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 captcha challenge result key.
Complete example on the ReAuthKit Login page:
Integration in Services
In addition to authentication as seen above, the captcha mechanism with Altcha can be integrated into any other type of service.
It is recommended to integrate the captcha into any public service that may be a target of attacks, such as exhaustive robot automation for mass data registration.
In the frontend, on the screen where the REST API service is integrated, the Altcha security widget must also be implemented; that is, wherever the service is called, the widget must be used.
Service Client
See below how to integrate Altcha into the Service Client.
Integrating the widget in the frontend is similar to what was already explained above in:
We need to obtain the widget payload, exactly as already exemplified above; it follows the same process as login authentication.
The payload is sent to the backend through a URL parameter, a form field, or even as a property in the JSON, depending on the HTTP method and how the service is implemented.
Example of how to send the payload to the registration service, or sign-up for 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 captcha challenge result key.
Complete example on the ReAuthKit Registration page:
Backend
With the payload being sent to the REST API service, then in the service code, on the backend side, we must validate it; see 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:
See more about low-code and polyglot programming features:
Conclusion
In a very simplified way, we can create a security mechanism in API services to prevent robots 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 enable and use this mechanism in any REST API service.
It is recommended that the Netuno auto-generated backoffice always has authentication with Altcha enabled in production, along with HTTPS and an SSL certificate.
Whenever possible, use Altcha to ensure security and robustness for your API's public services.