# django-ecp-auth

[![Python 3.12+](https://img.shields.io/badge/python-3.12%2B-blue.svg)](https://www.python.org/downloads/)
[![Django 5.0+](https://img.shields.io/badge/django-5.0%2B-green.svg)](https://www.djangoproject.com/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

**Django authentication package using Electronic Digital Signatures (ECP/ЕЦП).**

A reusable Django package that provides user authentication and registration using X.509 digital certificates and a challenge-response protocol. Built from scratch without any third-party ECP/signature libraries.

## Features

- 🔐 **PKCS#12 Certificate Authentication** — upload `.p12`/`.pfx` files for login
- 🛡️ **Challenge-Response Protocol** — secure nonce-based signature verification
- 📜 **X.509 Certificate Validation** — expiration, key usage, chain validation
- 🔑 **RSA & ECDSA Support** — SHA-256, SHA-384, SHA-512 hash algorithms
- 🧩 **Django Auth Backend** — integrates with Django's authentication system
- 🔗 **Custom Middleware** — ECP session management and cleanup
- 📋 **Django Admin** — manage user certificates from the admin panel
- 🎨 **Ready-to-use Templates** — Django DTL + Jinja2 templates included
- 📡 **Signals** — hook into auth events (login, logout, register)

## Installation

```bash
pip install django-ecp-auth
```

Or install from source:

```bash
git clone https://github.com/ecp-auth/django-ecp-auth.git
cd django-ecp-auth
pip install -e .
```

## Quick Start

### 1. Add to `INSTALLED_APPS`

```python
INSTALLED_APPS = [
    # ...
    'ecp_auth',
]
```

### 2. Add Authentication Backend

```python
AUTHENTICATION_BACKENDS = [
    'ecp_auth.backends.ECPAuthenticationBackend',
    'django.contrib.auth.backends.ModelBackend',  # Keep for admin login
]
```

### 3. Add Middleware

```python
MIDDLEWARE = [
    # ...
    'ecp_auth.middleware.ECPSessionMiddleware',
]
```

### 4. Include URL Patterns

```python
from django.urls import include, path

urlpatterns = [
    # ...
    path('auth/', include('ecp_auth.urls')),
]
```

### 5. Run Migrations

```bash
python manage.py migrate
```

### 6. Visit the Auth Pages

- **Login:** `http://localhost:8000/auth/login/`
- **Register:** `http://localhost:8000/auth/register/`

## Configuration

Add these settings to your `settings.py` (all are optional):

```python
# Challenge-Response
ECP_AUTH_CHALLENGE_TIMEOUT = 300        # Nonce timeout in seconds (default: 5 min)
ECP_AUTH_CHALLENGE_LENGTH = 32          # Nonce length in bytes (default: 32)

# Certificate Validation
ECP_AUTH_REQUIRE_KEY_USAGE = True       # Require digitalSignature key usage
ECP_AUTH_ALLOW_SELF_SIGNED = True       # Allow self-signed certificates
ECP_AUTH_VALIDATE_CHAIN = False         # Enable certificate chain validation
ECP_AUTH_TRUSTED_CA_DIR = '/path/to/ca/certs'  # Trusted CA directory

# Redirects
ECP_AUTH_LOGIN_REDIRECT_URL = '/'              # After login
ECP_AUTH_LOGOUT_REDIRECT_URL = '/auth/login/'  # After logout
```

## Authentication Flow

```
1. User uploads PKCS#12 file (.p12/.pfx) with password
2. Server extracts X.509 certificate and private key
3. Server validates the certificate (expiration, key usage, etc.)
4. Server generates a random challenge nonce
5. Server signs the nonce with the user's private key
6. User confirms the signature
7. Server verifies the signature against the certificate
8. User is authenticated and logged in
```

## Signals

Connect to authentication events:

```python
from ecp_auth.signals import ecp_login_success, ecp_user_registered

@receiver(ecp_login_success)
def on_ecp_login(sender, request, user, certificate_info, **kwargs):
    print(f"User {user.username} logged in via ECP")

@receiver(ecp_user_registered)
def on_ecp_register(sender, request, user, certificate_info, **kwargs):
    print(f"New user registered: {user.username}")
```

## Available Signals

| Signal | Arguments |
|--------|-----------|
| `ecp_login_success` | `request`, `user`, `certificate_info` |
| `ecp_login_failed` | `request`, `reason`, `certificate_info` |
| `ecp_user_registered` | `request`, `user`, `certificate_info` |
| `ecp_logout` | `request`, `user` |
| `ecp_certificate_linked` | `user`, `certificate_info` |

## Development

```bash
# Clone and install with dev dependencies
git clone https://github.com/ecp-auth/django-ecp-auth.git
cd django-ecp-auth
pip install -e ".[dev]"

# Run tests
pytest tests/ -v

# Run with coverage
pytest tests/ -v --cov=ecp_auth --cov-report=term-missing
```

## Dependencies

- **Django** ≥ 5.0
- **cryptography** ≥ 42.0
- **pycryptodome** ≥ 3.20
- **asn1crypto** ≥ 1.5
- **certvalidator** ≥ 0.11
- **Jinja2** ≥ 3.1

## License

MIT License. See [LICENSE](LICENSE) for details.
