Metadata-Version: 2.4
Name: sangho-oauth
Version: 0.2.0
Summary: Se connecter avec Sangho — client OAuth2/PKCE et provider django-allauth, séparés du SDK de paiement.
License: MIT
Keywords: sangho,oauth2,pkce,django-allauth,login
Classifier: Development Status :: 4 - Beta
Classifier: Framework :: Django
Classifier: Framework :: Django :: 4.2
Classifier: Framework :: Django :: 5.0
Classifier: Framework :: Django :: 5.1
Classifier: Framework :: Django :: 5.2
Classifier: Framework :: Django :: 6.0
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
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 :: Security
Classifier: Typing :: Typed
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpx>=0.24
Provides-Extra: allauth
Requires-Dist: django-allauth>=65.0; extra == "allauth"
Requires-Dist: requests>=2.28; extra == "allauth"
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: pytest-django>=4.8; extra == "dev"
Requires-Dist: responses>=0.25; extra == "dev"
Requires-Dist: django-allauth>=65.0; extra == "dev"
Requires-Dist: django>=4.2; extra == "dev"
Requires-Dist: requests>=2.28; extra == "dev"
Requires-Dist: ruff>=0.6; extra == "dev"
Requires-Dist: build>=1.2; extra == "dev"
Requires-Dist: twine>=5.0; extra == "dev"
Dynamic: license-file

# sangho-oauth (Python)

« Se connecter avec Sangho » — Authorization Code + PKCE (RFC 7636, méthode S256).

**Librairie distincte du SDK de paiement (`sangho`)** : OAuth identifie une personne, le SDK encaisse un
paiement. N'installez ceci que pour un bouton « Se connecter avec Sangho ».

Deux couches :

1. `sangho_oauth.SanghoOAuthClient` — client framework-agnostique (PKCE, échange de code, refresh, révocation,
   userinfo), sans Django. Utilisable avec Flask, FastAPI, un script…
2. `sangho_oauth.providers.sangho` — provider **django-allauth**, pour les projets Django, comme le provider GitHub
   ou Google. Il s'appuie sur la machinerie d'allauth (état anti-CSRF, PKCE, `complete_social_login`).

Python 3.10 à 3.14 ; Django 4.2, 5.2 et 6.x ; django-allauth 65. Testé localement sur les cinq versions de Python (3.14 en préversion `3.14.0rc2`), Django 4.2 (Python 3.10), 5.2 (3.10) et 6.1 (3.14) ; la CI (`.github/workflows/ci.yml`) couvre les combinaisons Python/Django supportées.

## Installation

```bash
pip install sangho-oauth              # client seul
pip install "sangho-oauth[allauth]"   # + provider django-allauth
```

## Prérequis chez Sangho

Créez un client OAuth dans le tableau de bord Sangho (Clés API → Clients OAuth) et enregistrez l'**URL de
redirection** exacte de votre site. Vous obtenez un `client_id` et un `client_secret` (affiché une seule fois). Le
client est *confidentiel* : le secret est **obligatoire** pour l'échange du code, le refresh et la révocation, et ne
doit jamais atteindre un navigateur.

## Django (django-allauth)

```python
# settings.py
INSTALLED_APPS += [
    "allauth", "allauth.account", "allauth.socialaccount",
    "sangho_oauth.providers.sangho",
]

SOCIALACCOUNT_PROVIDERS = {
    "sangho": {
        "APPS": [{
            "client_id": env("SANGHO_CLIENT_ID"),
            "secret": env("SANGHO_CLIENT_SECRET"),
            "key": "",
        }],
        "BASE_URL": "https://accounts.sangho.ga",   # hôte OAuth de Sangho (une base terminée par /o est acceptée)
        "REDIRECT_URI": "",                          # voir ci-dessous
        "TIMEOUT": 15,                               # secondes (jeton, profil)
        "VERIFY_SSL": True,                          # jamais False en production
    },
}

# urls.py
urlpatterns += [path("accounts/", include("allauth.urls"))]
```

```html
<a href="{% url 'sangho_login' %}">Se connecter avec Sangho</a>
```

| Réglage        | Défaut                    | Rôle |
| -------------- | ------------------------- | ---- |
| `BASE_URL`     | `https://accounts.sangho.ga`  | Hôte OAuth de Sangho ; les endpoints sont `<base>/o/authorize/`, `/o/token/`, `/o/userinfo/`. Pour un Sangho local : `https://accounts.sangho.com`. |
| `REDIRECT_URI` | vide                      | Vide : URL d'allauth, `<préfixe>/sangho/login/callback/`. Sinon **exactement** l'URL enregistrée chez Sangho ; l'alias `<préfixe>/sangho/callback/` est servi pour cela. |
| `TIMEOUT`      | celui d'allauth (5 s)     | Délai des appels vers Sangho. |
| `VERIFY_SSL`   | `True` (comportement `requests`) | `False` **en développement local seulement**, ou chemin d'un bundle de CA (ex. `mkcert`). |

Les réglages sont lus à l'exécution. Le délai et la vérification TLS sont appliqués **par la bibliothèque** : aucun
adaptateur social personnalisé n'est nécessaire dans votre projet.

### Vérifications de configuration

`python manage.py check` (et `check --deploy`) signale : `sangho_oauth.W001` (aucun `client_id`),
`E002` (`BASE_URL` non https), `E003` (`VERIFY_SSL=False`), `E004` (`REDIRECT_URI` non https) — les trois erreurs
sont ignorées quand `DEBUG=True`.

### Journaux

Les échecs sont journalisés avec leur cause réelle dans le logger `sangho_oauth` (ex.
`Sangho token request refused: HTTP 401 invalid_client`), là où allauth n'affiche qu'un message générique. Aucun
secret, code ni jeton n'est écrit dans les journaux.

### Sécurité

- L'identifiant de compte est `sub` (stable, opaque), jamais l'e-mail.
- L'e-mail n'est marqué vérifié que si Sangho renvoie `email_verified=true`. Ne liez pas automatiquement un compte
  existant par e-mail (`EMAIL_AUTHENTICATION: False`, valeur par défaut d'allauth pour ce provider).
- PKCE est toujours actif ; le serveur de Sangho l'exige.

## Usage direct (sans Django)

```python
from sangho_oauth import SanghoOAuthClient

sangho = SanghoOAuthClient(
    client_id="…", client_secret="…",
    redirect_uri="https://monapp.com/auth/sangho/callback",
    base_url="https://accounts.sangho.ga",
)

# 1. Redirection
auth_request = sangho.create_authorization_request()
session["oauth_code_verifier"] = auth_request.code_verifier
session["oauth_state"] = auth_request.state
return redirect(auth_request.url)

# 2. Callback
tokens = sangho.exchange_code(
    code=request.args["code"],
    code_verifier=session.pop("oauth_code_verifier"),
    received_state=request.args["state"],
    expected_state=session.pop("oauth_state"),
)
profile = sangho.get_user_info(tokens.access_token)   # profile["sub"] = identifiant stable à stocker
```

`SanghoOAuthClient` se ferme avec `close()` ou comme gestionnaire de contexte ; `verify=` et `http_client=` (un
`httpx.Client`) permettent un CA personnalisé, un proxy ou un transport de test.

### Erreurs

```python
from sangho_oauth import SanghoOAuthError

try:
    tokens = sangho.exchange_code(...)
except SanghoOAuthError as e:
    # e.code : "state_mismatch" (CSRF ou session expirée : ne PAS poursuivre), "invalid_client" (secret refusé),
    #          "invalid_grant" (code expiré ou déjà utilisé), "network_error", "invalid_response"…
    logger.error("Sangho OAuth: %s — %s", e.code, e.description)
```

## Scopes

| Scope     | Revendications ajoutées                        |
| --------- | ----------------------------------------------- |
| `profile` | `name`, `given_name`, `family_name`, `picture`   |
| `email`   | `email`, `email_verified`                        |

`sub` est toujours renvoyé.

## Développement et publication

Un `Makefile` regroupe toutes les commandes (`make help`). Il appelle `scripts/dev.py` (Python pur, sans shell POSIX) : ça
fonctionne donc sous Windows/PowerShell comme sous Linux et macOS, et `python scripts/dev.py <commande>` marche sans `make`.
Les environnements virtuels sont créés avec `uv` s'il est installé, sinon `python -m venv` (ou le lanceur `py -3.X` sous
Windows). Si `python` n'est pas le bon exécutable : `make test PYTHON=python3`.

```bash
make install                        # venv .venv + dépendances de dev
make test                           # pytest
make lint                           # ruff
make matrix                         # tests sur Python 3.10 → 3.14 (un venv .venv-X.Y par version, nécessite uv)
make venv PY=python3.14 VENV=.venv314   # venv pour une autre version de Python
make test DJANGO=">=4.2,<5.0"       # tests avec une version de Django précise
make check                          # lint + tests + build + twine check
make release                        # tag vX.Y.Z → .github/workflows/release.yml publie sur PyPI
```

La publication utilise le *trusted publishing* de PyPI (aucun jeton à stocker) : configurez-le une fois sur PyPI pour ce
dépôt (environnement `pypi`). À défaut : `make publish` avec `TWINE_USERNAME=__token__` et `TWINE_PASSWORD`.
