ALTCHA Library

2.0.0 · active · verified Fri Apr 17

The `altcha` library provides tools for creating and verifying ALTCHA challenges, a privacy-friendly, self-hosted, and free alternative to CAPTCHA. It allows Python applications to generate cryptographic proof-of-work challenges and validate responses from clients, protecting against bots and spam. The current version is 2.0.0, and it follows an infrequent but impactful release cadence, with major versions introducing API changes.

Common errors

Warnings

Install

Imports

Quickstart

This quickstart demonstrates how to initialize the `Altcha` object with a secret key, generate a challenge to be sent to a client, and then verify a client's response. It includes a hardcoded example of a valid client response for successful verification, and an example of an invalid response for error handling. Remember to replace the placeholder secret key with a strong, random key in production.

import os
import time
from altcha import Altcha, ChallengeResult

# Initialize Altcha with your secret key
# IMPORTANT: Replace 'altcha-dev-secret-key...' with a strong, random, 32+ character key
# and set it via an environment variable in production (e.g., ALTCHA_SECRET_KEY).
secret_key = os.environ.get('ALTCHA_SECRET_KEY', 'altcha-dev-secret-key-1234567890abcdef')

if secret_key == 'altcha-dev-secret-key-1234567890abcdef':
    print("WARNING: Using a default development secret key. Set ALTCHA_SECRET_KEY environment variable with a strong, random key in production!")

altcha = Altcha(secret_key)

# --- QUICKSTART PART 1: Generate a Challenge ---
print("\n--- Generating a Challenge ---")
# The 'challenge_obj' contains all necessary fields for the client.
challenge_obj = altcha.generate_challenge()
challenge_for_client = challenge_obj.to_json() # This JSON string is what you send to the client.
print(f"Generated Challenge (for client): {challenge_for_client}")

# In a real scenario, the client-side JavaScript library would solve this challenge
# and send back the original challenge data along with a computed 'response' string.

# --- QUICKSTART PART 2: Verify a Challenge Response ---
print("\n--- Verifying a Challenge Response ---")

# For demonstration, we'll use a hardcoded valid challenge/response pair.
# This 'solved_client_response' simulates what a client would send back after solving.
# The 'response' field comes from the client-side JS library's computation.
solved_client_response = {
    "challenge": "b4e4d7730e6a8e8073b64c748c5a21e421e421e4", 
    "signature": "283738b52a16d8a39e99279a059c259687e35b7501a4e1d1f042657d47833072",
    "algorithm": "sha1", 
    "salt": "altcha_salt",
    "expire": int(time.time() + 3600), # Ensure expiry is in the future for verification.
    "response": "sha1:1000:altcha_salt:s/h0QhQ2L2yYmYg5X2V5Q5R5R5Q5Y5h5y5Q5L2xY="
}

try:
    verification_result: ChallengeResult = altcha.verify(solved_client_response)
    if verification_result.verified:
        print("✅ Challenge Verified Successfully!")
    else:
        print(f"❌ Challenge Verification Failed: {verification_result.error}")
        # Common errors: 'challenge_expired', 'invalid_signature', 'incorrect_proof'
except ValueError as e:
    print(f"❌ Verification Error (ValueError): {e}")

# --- QUICKSTART PART 3: Demonstrate an Invalid Response ---
print("\n--- Demonstrating an Invalid Response ---")
# An example where the challenge hash is intentionally wrong.
invalid_response_data = {
    "challenge": "wrong_challenge_hash", 
    "signature": solved_client_response["signature"],
    "algorithm": solved_client_response["algorithm"],
    "salt": solved_client_response["salt"],
    "expire": solved_client_response["expire"],
    "response": solved_client_response["response"]
}

try:
    invalid_result = altcha.verify(invalid_response_data)
    if not invalid_result.verified:
        print(f"✅ Invalid challenge handled correctly. Error: {invalid_result.error}")
    else:
        print("❌ Unexpected: Invalid challenge was verified.")
except ValueError as e:
    print(f"✅ Invalid challenge handled correctly with ValueError: {e}")

view raw JSON →