Metadata-Version: 2.4
Name: django-altcha
Version: 1.0.0
Summary: Django field and widget for Altcha CAPTCHA.
Author-email: "nexB Inc." <info@nexb.com>
License-Expression: MIT
Project-URL: Homepage, https://github.com/aboutcode-org/django-altcha
Project-URL: Documentation, https://django-altcha.readthedocs.io/
Project-URL: Repository, https://github.com/aboutcode-org/django-altcha.git
Project-URL: Issues, https://github.com/aboutcode-org/django-altcha/issues
Project-URL: Changelog, https://github.com/aboutcode-org/django-altcha/blob/main/CHANGELOG.rst
Keywords: captcha,django,widget,form,altcha
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Web Environment
Classifier: Framework :: Django
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: Django>=4.2
Requires-Dist: altcha<2.0.0,>=1.0.0
Provides-Extra: dev
Requires-Dist: build; extra == "dev"
Requires-Dist: ruff; extra == "dev"
Requires-Dist: pytest-django; extra == "dev"
Provides-Extra: docs
Requires-Dist: Sphinx>=5.0.2; extra == "docs"
Requires-Dist: sphinx-rtd-theme>=1.0.0; extra == "docs"
Requires-Dist: sphinx-reredirects>=0.1.2; extra == "docs"
Requires-Dist: doc8>=0.11.2; extra == "docs"
Requires-Dist: sphinx-autobuild; extra == "docs"
Requires-Dist: sphinx-rtd-dark-mode>=1.3.0; extra == "docs"
Requires-Dist: sphinx-copybutton; extra == "docs"
Requires-Dist: myst-parser; extra == "docs"
Dynamic: license-file

# Django Altcha

**Django Altcha** is a Django library that provides easy integration of Altcha CAPTCHA
into your Django forms, enhancing user verification with configurable options.

By default, CAPTCHA validation operates in a **fully self-hosted mode**, 
**eliminating the need for external services** while ensuring privacy and control over
the verification process.

**Django Altcha** is **secure by default**, featuring built-in 
**protection against replay attacks** to ensure each challenge is validated only once. 
This helps safeguard your forms from repeated or spoofed submissions without 
requiring additional configuration.

## Installation

1. **Install the package:**

   ```bash
   pip install django-altcha
   ```

2. **Add to `INSTALLED_APPS`:**

   Update your Django project's `settings.py`:

   ```python
   INSTALLED_APPS = [
       # Other installed apps
       "django_altcha",
   ]
   ```

3. **Set your secret HMAC key:**
   
   This key is used to HMAC-sign ALTCHA challenges and **must be kept secret**.
   Treat it like a password: use a secure, 64-character hex string.

   Update your Django project's `settings.py`:

   ```python
   ALTCHA_HMAC_KEY="your_secret_hmac_key"
   ```

> [!NOTE]
> You can generate a new secured HMAC key using:
> ``python -c "import secrets; print(secrets.token_hex(64))"``

## Usage

### Adding the CAPTCHA Field to Your Form

To add the Altcha CAPTCHA field to a Django form, import `AltchaField` and add it to
your form definition:

```python
from django import forms
from django_altcha import AltchaField

class MyForm(forms.Form):
    captcha = AltchaField()
```

## Configuration Options

You can pass configuration options to `AltchaField` that are supported by Altcha.
These options are documented at
[Altcha's website integration guide](https://altcha.org/docs/website-integration/).

### Example with additional options:

```python
from django import forms
from django_altcha import AltchaField

class MyForm(forms.Form):
    captcha = AltchaField(
        floating=True,   # Enables floating behavior
        debug=True,      # Enables debug mode (for development)
        # Additional options supported by Altcha
    )
```

### Register a URL to Provide the Challenge

By default, challenge data is generated by the `AltchaField` and embedded directly 
into the rendered HTML using the `challengejson` option.

Alternatively, you can provide a URL that the Altcha widget’s JavaScript will fetch to 
retrieve the challenge, using the `challengeurl` option.

This approach is especially useful for enabling features like `refetchonexpire`, 
which **only work** when using a `challengeurl` (not `challengejson`).

A ready-to-use `AltchaChallengeView` is available in `django_altcha`. 
To enable it, register the view in your `urlpatterns`, for example:

```python
from django.urls import path
from django_altcha import AltchaChallengeView

urlpatterns += [
    path("altcha/challenge/", AltchaChallengeView.as_view(), name="altcha_challenge"),
]
```

Once the URL is registered, you can configure your `AltchaField` to use it via the 
`challengeurl` option:

```python
from django.urls import reverse_lazy
from django import forms
from django_altcha import AltchaField

class MyForm(forms.Form):
    captcha = AltchaField(
        challengeurl=reverse_lazy("altcha_challenge"),
    )
```

> [!NOTE]
> You can customize the challenge generation by passing options directly when 
> registering the view.
> For example: ``AltchaChallengeView.as_view(max_number=2000000)``

### Replay Attack Protection

Django Altcha **automatically protects against replay attacks** by ensuring each 
challenge can only be used once.
When a challenge is successfully validated, it is stored in a
cache and any subsequent attempt to reuse the same challenge will be rejected.

This protection is enabled by default and requires no additional configuration for
single-process deployments.

> [!IMPORTANT]
> By default, replay protection uses Django's `default` cache backend, which is
> `LocMemCache` unless you configure it otherwise. This in-memory cache is
> **not shared across workers**. If you run multiple workers (e.g., with gunicorn
> or uwsgi), configure a shared cache backend such as Redis or Memcached.

## Settings

### ALTCHA_HMAC_KEY

**Required.** This key is used to HMAC-sign ALTCHA challenges and **must be kept secret**.

### ALTCHA_CACHE_ALIAS

Django cache alias used for replay attack protection.
Defaults to `"default"`.

If your `default` cache is already a shared backend (Redis, Memcached, database),
no extra configuration is needed.

If you want to use a dedicated cache for ALTCHA, define one and point to it:

**Using Redis or Memcached:**

```python
CACHES = {
    'altcha': {
        'BACKEND': 'django.core.cache.backends.redis.RedisCache',
        'LOCATION': 'redis://127.0.0.1:6379',
    }
}
ALTCHA_CACHE_ALIAS = 'altcha'
```

**Using Database Caching:**

If you prefer not to set up Redis or Memcached, 
[Django's built-in database cache](https://docs.djangoproject.com/en/dev/topics/cache/#database-caching)
is a simple alternative:

```python
CACHES = {
    'altcha': {
        'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
        'LOCATION': 'altcha_cache',
    }
}
ALTCHA_CACHE_ALIAS = 'altcha'
```

Then create the cache table:

```bash
python manage.py createcachetable
```

### ALTCHA_CHALLENGE_EXPIRE

Challenge expiration duration in milliseconds.
Defaults to 20 minutes as per 
[Altcha security recommendations](https://altcha.org/docs/v2/security-recommendations/).

### ALTCHA_JS_URL

URL of the Altcha JavaScript file.
Defaults to the bundled django-altcha file.

### ALTCHA_INCLUDE_TRANSLATIONS

Whether to include [Altcha translations](https://altcha.org/docs/v2/widget-integration/#internationalization-i18n).
Defaults to `False`.

### ALTCHA_JS_TRANSLATIONS_URL

URL of the Altcha translations JavaScript file.
Defaults to the bundled django-altcha file.

Only loaded when `ALTCHA_INCLUDE_TRANSLATIONS` is `True`.

### ALTCHA_VERIFICATION_ENABLED

Set to `False` to skip Altcha validation altogether.
Defaults to `True`.

## Logging

Django Altcha uses the standard Python `logging` module under the logger name
`django_altcha`. No logs are emitted under normal operation; logging fires only
on validation failures and misconfiguration.

### What gets logged

- **WARNING** on invalid or missing CAPTCHA tokens submitted to a form.
- **WARNING** on replay attempts (a challenge reused after it has already been validated).
- **ERROR** when `ALTCHA_HMAC_KEY` is not configured.
- **Exception with traceback** when verification or payload decoding raises
  unexpectedly.

Payloads, challenge values, and the HMAC key are never included in log
messages.

### Enabling logs

Add the `django_altcha` logger to your project's `LOGGING` setting:

```python
LOGGING = {
    "version": 1,
    "disable_existing_loggers": False,
    "handlers": {
        "console": {
            "class": "logging.StreamHandler",
        },
    },
    "loggers": {
        "django_altcha": {
            "handlers": ["console"],
            "level": "WARNING",
        },
    },
}
```

Set `"level": "ERROR"` to see only misconfiguration and unexpected failures,
or `"level": "DEBUG"` to see additional diagnostic messages during development.

## Contributing

We welcome contributions to improve this library.
Feel free to submit issues or pull requests!

## License

This project is licensed under the **MIT License**.
See the [LICENSE](https://github.com/aboutcode-org/django-altcha/blob/main/LICENSE) 
file for details.
