Altcha for simple captcha

53 views
Skip to first unread message

Jacinto Parga

unread,
Aug 2, 2025, 1:27:35 PMAug 2
to py4web
I use this captcha solution for simple applications. I share it with you although I have not tested it deeply.

Information in:  https://altcha.org/

Previous: 
python3 -m pip install --upgrade altcha


In controller  

from altcha import (

   create_challenge,

   verify_solution,

)


from .settings import ALTCHA_HMAC_KEY # I have put it in settings


@action("altcha", method=["GET"])

def get_altcha():

   try:

       challenge = create_challenge(

           ChallengeOptions(

               hmac_key=ALTCHA_HMAC_KEY,

               max_number=50000,

           )

       )

       response.headers["Content-Type"] = "application/json"

       print("Created challenge: ", challenge.__dict__)

       return challenge.__dict__

   except Exception as e:

       response.status = 500

       return {"error": f"Failed to create challenge: {str(e)}"}


@action.uses('form_altcha.html', session, flash)

def some_form():

   fields = [

       Field("name", requires=IS_NOT_EMPTY()),

       Field("color", type="string", requires=IS_IN_SET(["red","blue","green"])),

   ]

   form = Form(fields,

               csrf_session=session,

               submit_button=T("Submit"))


   form.structure.insert(-1,XML('<altcha-widget></altcha-widget></br>'))

   if form.accepted:

       altcha_payload = request.POST.get("altcha")

       if not altcha_payload:

           response.status = 400

           print("NO ALTCHA payload")

           flash.set("NO ALTCHA payload")

       else:

           ok, error = verify_solution(altcha_payload, ALTCHA_SECRET_KEY)

           if not ok:

               response.status = 400

               print("ALTCHA verification fail:", error)

               flash.set("ALTCHA verification fail: {error}".format(error=error))

           else:

               flash.set("ALTCHA verified.")

       

   return dict(form=form)



The template:

[[extend 'layout.html']]


<script async defer src="https://cdn.jsdelivr.net/gh/altcha-org/altcha/dist/altcha.min.js" type="module"></script>

<script>

 document.addEventListener("DOMContentLoaded", function () {

   const altchaWidget = document.querySelector("altcha-widget");

   if (altchaWidget) {

     altchaWidget.setAttribute("challengeurl", "[[=URL('altcha')]]");

   }

 });

</script>

<div class="section">

 <div class="vars">[[=form]]</div>

</div>



Auth form:


[[extend "layout.html"]]

<script async defer src="https://cdn.jsdelivr.net/gh/altcha-org/altcha/dist/altcha.min.js" type="module"></script>


<script>

 document.addEventListener("DOMContentLoaded", function () {

   const altchaWidget = document.querySelector("altcha-widget");

   if (altchaWidget) {

     altchaWidget.setAttribute("challengeurl", "[[=URL('altcha')]]");

   }

 });

</script>

<style>

 .auth-container {

   max-width: 80%;

   min-width: 400px;

   margin-left: auto;

   margin-right: auto;

   border: 1px solid #e1e1e1;

   border-radius: 10px;

   padding: 20px;

 }

</style>


[[form.structure.insert(-1,XML('<altcha-widget></altcha-widget></br>'))]]


<div class="auth-container">[[=form]]</div>




Massimo

unread,
Aug 3, 2025, 6:07:51 PMAug 3
to py4web
Would you mind adding this to the manual?

Jacinto Parga

unread,
Aug 4, 2025, 2:55:45 PMAug 4
to py4web
Yes, for sure.

Jacinto Parga

unread,
Aug 12, 2025, 4:53:17 PMAug 12
to py4web
Added a fixture to make it work in auth.

class AltchaServerFixture(Fixture):
    def __init__(self, hmac_key=ALTCHA_HMAC_KEY):
        super().__init__()
        self._name = "altcha_server"
        self.hmac_key = hmac_key

    def on_success(self, context):
        # Only verify Altcha for POST requests
        if request.method != "POST":
            return

        payload = request.POST.get("altcha")
        if not payload:
            raise HTTP(400, "ALTCHA payload not received")
        try:
            verified, err = verify_solution(payload, self.hmac_key, True)
            if not verified:
                raise HTTP(400, "Invalid ALTCHA")
            return {"success": True, "message": "Altcha verification passed"}
        except Exception as e:
            raise HTTP(500, "Exception in Altcha verification")

So in common.py:

from .xxxxxxx import AltchaServerFixture
auth.enable(uses=(session, T, db, AltchaServerFixture()), env=dict(T=T))
Reply all
Reply to author
Forward
0 new messages