Metadata-Version: 2.4
Name: baobab-auth-client
Version: 0.1.0
Summary: Client Python d'intégration à baobab-auth-api : validation JWT/JWKS, rôles et permissions, dépendances FastAPI.
Project-URL: Homepage, https://github.com/baobabgit/baobab-auth-client
Project-URL: Repository, https://github.com/baobabgit/baobab-auth-client
Project-URL: Documentation, https://baobab-auth-client.readthedocs.io
Project-URL: Issues, https://github.com/baobabgit/baobab-auth-client/issues
Author: Patrick ANDRIANAIVO
License: MIT License
        
        Copyright (c) 2026 Michel ANDRIANAIVO
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
License-File: LICENSE
Keywords: auth,authentication,baobab,client,fastapi,jwks,jwt
Classifier: Framework :: FastAPI
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Security
Classifier: Typing :: Typed
Requires-Python: >=3.11
Provides-Extra: dev
Requires-Dist: bandit[sarif,toml]>=1.7; extra == 'dev'
Requires-Dist: fastapi>=0.110; extra == 'dev'
Requires-Dist: httpx>=0.27; extra == 'dev'
Requires-Dist: mypy>=1.11; extra == 'dev'
Requires-Dist: pip-audit>=2.7; extra == 'dev'
Requires-Dist: pre-commit>=3.8; extra == 'dev'
Requires-Dist: pyjwt[crypto]>=2.8; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: respx>=0.21; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Provides-Extra: docs
Requires-Dist: furo>=2024.0; extra == 'docs'
Requires-Dist: sphinx>=7.0; extra == 'docs'
Provides-Extra: fastapi
Requires-Dist: fastapi>=0.110; extra == 'fastapi'
Provides-Extra: http
Requires-Dist: httpx>=0.27; extra == 'http'
Provides-Extra: jwt
Requires-Dist: pyjwt[crypto]>=2.8; extra == 'jwt'
Provides-Extra: testing
Requires-Dist: pytest-asyncio>=0.23; extra == 'testing'
Requires-Dist: pytest>=8.0; extra == 'testing'
Requires-Dist: respx>=0.21; extra == 'testing'
Description-Content-Type: text/markdown

# baobab-auth-client

[![CI](https://github.com/baobabgit/baobab-auth-client/actions/workflows/ci.yml/badge.svg)](https://github.com/baobabgit/baobab-auth-client/actions/workflows/ci.yml)
[![codecov](https://codecov.io/gh/baobabgit/baobab-auth-client/branch/main/graph/badge.svg)](https://codecov.io/gh/baobabgit/baobab-auth-client)
[![PyPI version](https://img.shields.io/pypi/v/baobab-auth-client.svg)](https://pypi.org/project/baobab-auth-client/)
[![Python versions](https://img.shields.io/pypi/pyversions/baobab-auth-client.svg)](https://pypi.org/project/baobab-auth-client/)
[![Documentation Status](https://readthedocs.org/projects/baobab-auth-client/badge/?version=latest)](https://baobab-auth-client.readthedocs.io/en/latest/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
[![Checked with mypy](https://www.mypy-lang.org/static/mypy_badge.svg)](https://mypy-lang.org/)
[![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit)](https://github.com/pre-commit/pre-commit)
[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg)](https://conventionalcommits.org)

> Librairie cliente Python d'intégration à **`baobab-auth-api`** : validation
> cryptographique des access tokens JWT (via JWKS), construction d'un utilisateur
> authentifié, vérification locale des rôles et permissions, et dépendances FastAPI
> prêtes à l'emploi.

> ℹ️ Projet en cours de construction (version cible `0.1.0`). Le découpage du besoin
> en User Stories / Features vit dans [`docs/specifications/`](docs/specifications/) ;
> le suivi de réalisation dans les Issues / Project GitHub.

## Table des matières

- [À propos](#à-propos)
- [Fonctionnalités](#fonctionnalités)
- [Stack technique](#stack-technique)
- [Architecture](#architecture)
- [Structure du projet](#structure-du-projet)
- [Démarrage rapide](#démarrage-rapide)
- [Configuration](#configuration)
- [Sécurité](#sécurité)
- [Contribuer](#contribuer)
- [Et après ?](#et-après-)
- [Licence](#licence)
- [Remerciements](#remerciements)
- [Auteur](#auteur)

## À propos

`baobab-auth-client` est la brique de l'écosystème **Baobab** destinée aux APIs
métiers (FastAPI ou Python async) qui veulent **déléguer l'authentification** à
`baobab-auth-api` sans embarquer la persistance, le hash des mots de passe, la
génération des tokens ni la rotation des clés. Elle valide les access tokens JWT,
récupère et met en cache les clés publiques JWKS, construit un utilisateur
authentifié exploitable et contrôle localement rôles et permissions.

Son périmètre est **volontairement borné** : elle ne stocke rien, ne génère aucun
token, ne hashe aucun mot de passe et n'expose aucun serveur HTTP.

## Fonctionnalités

- 🔐 **Validation JWT via JWKS** : signature, expiration, issuer, audience,
  algorithmes autorisés (jamais `alg=none`).
- 🗝️ **Cache JWKS en mémoire** : TTL configurable, refresh forcé sur `kid`
  inconnu, protection anti-abus.
- 👤 **Utilisateur authentifié** immuable (`AuthenticatedUser`) avec helpers de
  rôles et permissions.
- 🌐 **Client HTTP async** (`AuthClient`) : login, refresh, logout, me, jwks, health.
- ⚡ **Dépendances FastAPI** prêtes à l'emploi (401/403) en extra optionnel.
- 🧪 **Helpers de test** pour tester les routes protégées sans serveur auth.
- 🧱 **Cœur léger et typé** (`py.typed`), dépendances lourdes en extras.

## Stack technique

| Domaine        | Outil                                  |
| -------------- | -------------------------------------- |
| Langage        | Python ≥ 3.11                          |
| HTTP           | `httpx` (async)                        |
| JWT / JWKS     | `pyjwt[crypto]`                        |
| Intégration    | `fastapi` (extra optionnel)            |
| Environnement  | `venv` (`.venv`) + `pip`               |
| Lint & format  | `ruff`                                 |
| Typage         | `mypy` (strict)                        |
| Tests          | `pytest` + `pytest-asyncio` + `respx`  |
| Documentation  | `sphinx` (+ `furo`), reStructuredText  |
| CI / Hooks     | GitHub Actions, `pre-commit`           |

## Architecture

Deux axes orthogonaux structurent le projet :

- **Le « quoi »** : `Cahier des charges → US → FEAT → Task` (arbre de besoin).
- **Le « quand »** : sprints (champ *Iteration* de GitHub Projects).

```
Besoin (docs/specifications, RST)
   └─ US-001 ──► Issue ─┐
        └─ FEAT-001.1 ──► sub-issue ─┐  ◄── code (src/) + tests (tests/, miroir)
             └─ TASK-001.1.1 ──► sub-issue ──► sprint (Iteration)
```

## Structure du projet

```
.
├── AGENTS.md              # Règles de dev — SOURCE UNIQUE DE VÉRITÉ
├── CLAUDE.md             # Adaptateur Claude Code (importe AGENTS.md)
├── .cursor/rules/        # Adaptateur Cursor (reflète AGENTS.md)
├── src/baobab_auth_client/  # Code (1 classe par fichier)
├── tests/baobab_auth_client/# Tests en miroir de src/
├── docs/                 # Sphinx : specifications/ · api/ · guides/
│   └── workflow/         # Process multi-IA : rôles, gates, handoff, prompts
├── .github/              # CI + templates d'issues (US/FEAT/Task) + PR
├── pyproject.toml        # Config unique (projet, ruff, mypy, pytest, coverage)
├── .pre-commit-config.yaml
└── Makefile              # Commandes standard (install, lint, type, test, docs)
```

## Démarrage rapide

```bash
# 1. Cloner
git clone https://github.com/baobabgit/baobab-auth-client.git
cd baobab-auth-client

# 2. Environnement virtuel (toujours .venv)
python -m venv .venv
# Windows : .venv\Scripts\Activate.ps1   |   Linux/macOS : source .venv/bin/activate

# 3. Installer + hooks
pip install -e ".[dev,docs]"
pre-commit install

# 4. Vérifier
ruff check . && mypy && pytest      # ou : make check
```

## Utilisation

### Installation (API consommatrice)

```bash
pip install baobab-auth-client[http,jwt,fastapi]
```

Les dépendances lourdes sont en **extras** : `http` (httpx), `jwt` (pyjwt[crypto]),
`fastapi` (intégration), `testing` (pytest/respx). Le cœur reste léger.

### Protéger des routes FastAPI

```python
from fastapi import Depends, FastAPI
from baobab_auth_client.fastapi import create_auth_dependencies_from_settings
from baobab_auth_client.models import AuthenticatedUser

app = FastAPI()
auth = create_auth_dependencies_from_settings()  # lit les variables BAOBAB_AUTH_*

@app.get("/me")
async def me(user: AuthenticatedUser = Depends(auth.require_user)):
    return {"id": user.id, "roles": user.roles}

@app.get("/admin")
async def admin(user: AuthenticatedUser = Depends(auth.require_role("ADMIN"))):
    return {"ok": True}

@app.post("/sync")
async def sync(user: AuthenticatedUser = Depends(auth.require_permission("auth:sync:run"))):
    return {"status": "started"}
```

Token absent/invalide → **401** ; rôle/permission insuffisant → **403** ; service
d'authentification indisponible → **503**.

### Valider un token hors FastAPI

```python
from baobab_auth_client.config import AuthClientSettings
from baobab_auth_client.jwks import JwksCache, JwksFetcher
from baobab_auth_client.tokens import TokenValidator

settings = AuthClientSettings(auth_base_url="http://auth-api:8000", issuer="baobab-auth")
validator = TokenValidator(settings, JwksCache(JwksFetcher(settings)))

user = await validator.validate_access_token(access_token)
assert user.has_permission("collection:read")
```

### Consommer `baobab-auth-api`

```python
from baobab_auth_client.client import AuthClient

async with AuthClient(base_url="http://auth-api:8000") as client:
    tokens = await client.login(email="user@example.com", password="secret")
    me = await client.me(access_token=tokens.access_token)
```

### Tester une route protégée (sans serveur auth)

```python
from baobab_auth_client.fastapi import create_auth_dependencies_from_settings
from baobab_auth_client.testing import make_authenticated_user, fake_require_user

auth = create_auth_dependencies_from_settings(settings)
app.dependency_overrides[auth.require_user] = fake_require_user(
    make_authenticated_user(id="user-1", roles=("USER",), permissions=("collection:read",))
)
```

## Configuration

La configuration cible (`AuthClientSettings`) se renseigne par des **variables
d'environnement** `BAOBAB_AUTH_*`. Copiez le modèle et renseignez vos valeurs :

```bash
cp .env.example .env
```

| Variable                         | Description                          | Défaut   |
| -------------------------------- | ------------------------------------ | -------- |
| `BAOBAB_AUTH_BASE_URL`           | URL de base de `baobab-auth-api`     | —        |
| `BAOBAB_AUTH_ISSUER`             | Issuer attendu (`iss`)               | —        |
| `BAOBAB_AUTH_AUDIENCE`           | Audience attendue (`aud`)            | —        |
| `BAOBAB_AUTH_ALGORITHMS`         | Algorithmes autorisés                | `RS256`  |
| `BAOBAB_AUTH_JWKS_CACHE_TTL_SECONDS` | TTL du cache JWKS (s)            | `300`    |

> Le détail complet de la configuration sera documenté dans `docs/guides/`
> (feature `FEAT-001.2`).

## Sécurité

- **Aucun secret** dans le code ni dans Git : `.env` est gitignoré ; seul
  `.env.example` (sans valeurs) est versionné.
- Le hook `detect-private-key` et `pre-commit` bloquent les fuites évidentes.
- Analyse `bandit` (SAST) + `pip-audit` (vulns des dépendances) + Dependabot.
- Signalez toute vulnérabilité en privé (voir [`SECURITY.md`](SECURITY.md)) plutôt
  que via une issue publique.

## Contribuer

Les règles de développement sont décrites dans [`AGENTS.md`](AGENTS.md) et le
processus dans [`CONTRIBUTING.md`](CONTRIBUTING.md). En résumé : branche dédiée,
**Conventional Commits** avec ID spec, PR verte (lint + types + tests ≥ 90 %).

## Et après ?

- [x] Brancher le dépôt sur GitHub Projects (US / FEAT / Task + sprints).
- [x] Publier la documentation (Read the Docs) — config `.readthedocs.yaml`
  versionnée ; importer le projet sur [readthedocs.org](https://readthedocs.org/dashboard/import/)
  (slug `baobab-auth-client`) pour activer le build automatique.
- [x] Ajouter la publication PyPI au workflow (`release.yml`, OIDC/Trusted Publishing).

## Licence

Distribué sous licence **MIT**. Voir [`LICENSE`](LICENSE).

## Remerciements

Outils et standards qui rendent ce template possible : Python, Ruff, mypy,
pytest, Sphinx, pre-commit, GitHub Actions, et les documentations de Claude Code,
Cursor et OpenAI Codex.

## Auteur

**Patrick ANDRIANAIVO** — [patrick.andri@gmail.com](mailto:patrick.andri@gmail.com)
