---
title: "oauthsonas"
description: "Fournisseur OpenID Connect en mémoire pour le développement local et les tests d'intégration : Authorization Code + S256 PKCE, JWT RS256, personas et refresh tokens rotatifs."
canonical_url: https://optimi.com/fr/docs/oauthsonas
md_url: https://optimi.com/fr/docs/oauthsonas.md
last_updated: 2026-09-04
---

# oauthsonas

`oauthsonas` est un petit **fournisseur OpenID Connect** en mémoire pour le
développement local et les tests d'intégration. Il implémente un vrai flux
navigateur Authorization Code avec S256 PKCE, des refresh tokens rotatifs, des
JWT d'accès et d'identité signés RS256, la découverte, JWKS, userinfo, logout et
des personas configurables.

- **Module :** [`github.com/optimiweb/oauthsonas`](https://github.com/optimiweb/oauthsonas)
- **Install :** `go install github.com/optimiweb/oauthsonas/cmd/oauthsonas@latest`
- **Licence :** MIT
- **Issuer par défaut :** `http://127.0.0.1:8181` (correspondance exacte — ne
  substituez pas `localhost`)

## Avertissement de sécurité

**Usage développement et test uniquement. N'exposez jamais ce serveur à
Internet et ne le déployez pas en production.**

Il dispose d'un sélecteur de personas en mémoire à la place d'une vraie
authentification, génère une nouvelle clé de signature à chaque démarrage, et
n'a pas d'état persistant. Il se lie à `127.0.0.1` par défaut et refuse les
adresses non-loopback sauf si `OAUTHSONAS_ALLOW_NON_LOOPBACK=true` est défini
explicitement.

Lisez la [politique de sécurité](https://github.com/optimiweb/oauthsonas/blob/main/SECURITY.md)
avant de signaler une vulnérabilité.

## À quoi ça sert

- Remplacer un fournisseur OIDC externe en développement local et en CI **sans**
  court-circuiter le vrai flux navigateur de la partie cliente.
- Exercer la découverte, Authorization Code + S256 PKCE, la préservation du
  `state` de callback, l'échange de tokens, la validation JWKS, userinfo, logout
  et la rotation des refresh tokens.
- Fournir des claims applicatifs stables (`roles`, `org_id` optionnel,
  `memberships` optionnel) avec des noms de claims configurables.
- Garder des identités reproductibles en YAML tout en laissant l'autorisation
  applicative et l'expansion rôle → permissions à la partie cliente.

## Ce que ce n'est pas

Il n'y a intentionnellement **pas** de base de données, de flux mot de passe,
d'API d'admin, d'UI de provisioning, d'API Organizations / Management Auth0, de
fournisseur d'identité externe, ni de manifeste de déploiement production. Tout
l'état protocole est en mémoire — redémarrez le processus pour effacer codes et
tokens émis.

## Démarrage rapide

1. Démarrez le fournisseur avec le client et les personas d'exemple :

   ```sh
   go run ./cmd/oauthsonas --config config.example.yaml
   ```

2. Pointez votre dashboard (ou autre RP) vers les variables de
   l'[exemple dashboard](#exemple-dashboard).
3. Lancez le flux de login et choisissez un persona sur la page d'autorisation
   locale (par exemple **Acme Administrator**).
4. Le callback reçoit un code d'autorisation normal ; la bibliothèque OIDC
   l'échange et valide les tokens RS256 contre le JWKS publié.

Valider la configuration sans démarrer le serveur :

```sh
go run ./cmd/oauthsonas --config config.example.yaml --check-config
```

Afficher la version :

```sh
oauthsonas --version
```

Découverte :

```text
http://127.0.0.1:8181/.well-known/openid-configuration
```

## Exécuter dans un conteneur

```sh
podman build -t oauthsonas -f Containerfile .
podman run --rm -p 127.0.0.1:8181:8181 \
  -e OAUTHSONAS_ALLOW_NON_LOOPBACK=true oauthsonas
```

Docker utilise les mêmes commandes avec `docker` à la place de `podman`.

L'image se lie à `0.0.0.0` pour que le port-forwarding fonctionne. Vous devez
définir `OAUTHSONAS_ALLOW_NON_LOOPBACK=true` pour reconnaître l'exposition.
Gardez le port **publié** lié à la loopback (`-p 127.0.0.1:8181:8181`).

Images publiées (après un tag Git SemVer) :

```sh
podman pull ghcr.io/optimiweb/oauthsonas:1.2.3
podman run --rm -p 127.0.0.1:8181:8181 \
  -e OAUTHSONAS_ALLOW_NON_LOOPBACK=true \
  ghcr.io/optimiweb/oauthsonas:1.2.3
```

Pousser `v1.2.3` publie `1.2.3`, `1.2`, `1` et `latest`. Les tags pre-release
tels que `v1.2.3-rc.1` publient uniquement leur version exacte et ne déplacent
pas `latest`.

## Exemple dashboard

Configurez un dashboard navigateur avec l'équivalent de sa bibliothèque OIDC :

```sh
OIDC_AUTHORITY=http://127.0.0.1:8181
OIDC_CLIENT_ID=dashboard
OIDC_REDIRECT_URI=http://127.0.0.1:5173/auth/callback
OIDC_POST_LOGOUT_REDIRECT_URI=http://127.0.0.1:5173/
OIDC_SCOPE="openid profile email"
OIDC_AUDIENCE=https://api.optimicdn.test
```

Le client d'exemple est **public** et exige **S256 PKCE**. La page
d'autorisation laisse un développeur choisir un persona configuré, puis
redirige vers le callback du dashboard avec un code d'autorisation normal et un
`state` inchangé.

Pour clients, personas, noms de claims et TTL, voir
**[Configuration](/fr/docs/oauthsonas/configuration)**.

## Comportement protocole

- **Flux :** Authorization Code avec S256 PKCE obligatoire, `response_type=code`
  et scope `openid`.
- **Scopes :** `openid`, `profile`, `email`, et optionnellement `offline_access`.
- **Audience :** optionnelle à l'authorize ; si fournie elle doit égaler
  exactement `api_audience`. Les access tokens utilisent toujours cette
  audience API.
- **Refresh :** `offline_access` émet un refresh token. Fosite fait tourner les
  refresh tokens et détecte/rejette la réutilisation. Fournir `scope` au refresh
  ne peut que réduire la portée ; l'ensemble plus étroit devient le grant du
  token rotaté.
- **Codes :** courte durée de vie et à usage unique. Le `state` est renvoyé
  inchangé. Les nonces sont copiés dans les ID tokens.
- **Userinfo :** `GET` ou `POST /userinfo` valide le bearer access token et
  renvoie le sous-ensemble de claims profil/personnalisés accordé.
- **Logout :** `GET /logout` ne redirige que vers un
  `post_logout_redirect_uri` enregistré. Avec `id_token_hint`, le client est
  dérivé de l'`aud` du token (logout initié par le RP sans `client_id` explicite).

## Endpoints

| Méthode | Chemin | Rôle |
| --- | --- | --- |
| `GET` | `/healthz` | Liveness |
| `GET` | `/readyz` | Readiness |
| `GET` | `/.well-known/openid-configuration` | Découverte |
| `GET` | `/.well-known/jwks.json` | Clés publiques de signature |
| `GET` | `/oauth2/auth` | Autorisation (sélecteur de personas) |
| `POST` | `/oauth2/auth/select` | Sélection de persona |
| `POST` | `/oauth2/token` | Endpoint token |
| `GET` \| `POST` | `/userinfo` | Userinfo |
| `GET` | `/logout` | Logout initié par le RP |

## Santé et readiness

`GET /healthz` renvoie 200 :

```json
{"status":"ok","version":"1.0.0","issuer":"http://127.0.0.1:8181","uptime":"5m23s"}
```

`GET /readyz` renvoie 200 une fois le serveur prêt :

```json
{"status":"ready","issuer":"http://127.0.0.1:8181"}
```

Les deux définissent `Cache-Control: no-store`. Utilisez-les pour les probes
Kubernetes ou le signalement d'orchestration.

## Journalisation structurée

Le serveur utilise `log/slog` en JSON (défaut) ou texte :

```text
--log-format json|text   (défaut : json)
--log-level  debug|info|warn|error   (défaut : info)
```

Attributs de requête : `method`, `path`, `status`, `duration`. Les événements
OAuth ajoutent `client_id`, des champs grant/persona, et un `interaction_id`
tronqué le cas échéant. **Jamais journalisés :** codes d'autorisation,
access/refresh/ID tokens, cookies, valeurs CSRF ou vérificateurs PKCE.

## Contraintes Kubernetes

oauthsonas est conçu pour des déploiements **single-replica** uniquement :

- **Une réplique max.** Tokens, codes, interactions et clés de signature sont
  locaux au processus. Plusieurs répliques provoquent des décalages de cookies
  et un JWKS invalide.
- **Pas de rolling surge.** Utilisez `strategy: Recreate`. Un second pod
  génèrerait une clé différente et casserait la validation.
- **Pas d'équilibrage de charge partagé** entre instances.
- **Un redémarrage invalide tout** — tous les tokens et codes émis sont perdus.
- Utilisez **`/healthz` pour la liveness** et **`/readyz` pour la readiness**.

Le serveur garbage-collecte l'état expiré en mémoire. Pour les instances
longues, envisagez un cycle de vie de processus qui redémarre périodiquement le
pod.

## Automation navigateur

Le sélecteur de personas expose des sélecteurs stables pour Playwright,
Cypress et outils similaires.

| Élément | Sélecteur |
| --- | --- |
| Formulaire persona | `form[data-testid="persona-form-<id>"]` ou `.persona-form` |
| Champ CSRF | `input[data-testid="csrf-input"]` |
| Interaction ID | `input[data-testid="interaction-id-input"]` |
| Soumission | `button[data-testid="persona-select-<id>"]` |

### Flux

1. Naviguez vers `GET /oauth2/auth?…` avec les paramètres OIDC standard.
2. La page contient un `<form>` par persona.
3. Extrayez `csrf` et `interaction_id` des champs cachés.
4. `POST /oauth2/auth/select` avec `csrf`, `interaction_id` et `persona`.
5. Attendez un 302 vers le `redirect_uri` enregistré avec `code` et `state`.
6. Échangez le code à `POST /oauth2/token`.

Exigences : inclure le cookie `oauthsonas_interaction_<id>` de l'étape 1 ; le
CSRF est à usage unique (replay → 403) ; paramètres de formulaire en double →
400.

## Tester et contribuer

```sh
go test -race ./...
go vet ./...
```

Voir [CONTRIBUTING.md](https://github.com/optimiweb/oauthsonas/blob/main/CONTRIBUTING.md)
pour les guidelines de pull request.

## Suite

- **[Configuration](/fr/docs/oauthsonas/configuration)** — clients YAML,
  personas, claims et TTL.
- **[Vue d'ensemble open source](/fr/docs/open-source)** — autres projets
  Optimi.
