
# Créer ou remplacer la configuration OIDC d'une connexion

`PUT /v1/connections/{connection_id}/oidc` · autorisation `connections:write`

Crée ou remplace entièrement la configuration de partie utillisatrice (relying party) d'une connexion OIDC d'entreprise.

- **Première écriture :** n'envoyez aucun en-tête `If-Match` et indiquez `sp_host` (un hôte actif de cet environnement). L'URL de rappel `https://<sp_host>/sso/<connection_id>/callback` est fixée dès lors. La réponse est `201 Created`. Envoyer `If-Match` alors qu'aucune configuration n'existe échoue avec `412`.
- **Mise à jour :** `If-Match` est requis. Sans lui, la requête échoue avec `428` et rien n'est écrit : une requête sans cet en-tête ne peut donc jamais écraser une configuration existante. Si la configuration a changé depuis votre lecture (ou si la valeur ne correspond pas), la requête échoue avec `412`. `*` correspond à la configuration actuelle. `sp_host` doit être vide ou égal à l'hôte enregistré.

Le secret client est en écriture seule : il n'est jamais renvoyé, journalisé ni écrit dans le journal d'audit. Lorsque vous mettez à jour sans `client_secret`, le secret enregistré est conservé ; `private_key_jwt` le supprime (la clé de signature de l'environnement signe l'assertion client). Un `issuer` modifié efface le document de découverte enregistré ; un `issuer` ou un `client_id` modifié invalide le dernier test de connexion réussi. Envoyer la même configuration n'écrit rien. Les écritures sont journalisées sous `connection.protocol_config_created` ou `connection.oidc_config_updated` (seuls les champs modifiés ; le secret n'apparaît que sous `client_secret_set`).

Sur une connexion **active**, une mise à jour qui modifie `issuer` ou `client_id` n'est pas appliquée. Toute la requête (y compris un nouveau secret) devient une [révision en brouillon](../connections/revisions.md) : la réponse est `200 OK` avec la configuration active inchangée, son `ETag` inchangé et un champ `revision`. Testez le brouillon avec [Tester une connexion](../connections/test.md) et `revision_id`, puis [publiez-le](../connections/publish-revision.md). Les autres mises à jour s'appliquent directement.

## Requête

| Champ | Type | Obligatoire | Description |
|---|---|---|---|
| `connection_id` | path | oui | Identifiant de la connexion OIDC d'entreprise (`conn_…`). |
| `If-Match` | header | en mise à jour | L'`ETag` de la dernière lecture ou écriture. Absent en mise à jour : `428` ; obsolète : `412`. Non envoyé lors de la première écriture. |
| `sp_host` | string | à la première écriture | Hôte actif de cet environnement (minuscules). En mise à jour : vide ou égal à l'hôte enregistré. |
| `issuer` | string | oui | URL `https` absolue sans requête ni fragment, de 512 octets au maximum. |
| `client_id` | string | oui | De 1 à 255 octets, sans caractères de contrôle. |
| `client_auth_method` | string | oui | `private_key_jwt` (recommandé ; aucun secret n'est stocké), `client_secret_basic` ou `client_secret_post`. |
| `client_secret` | string | non | En écriture seule, 1024 octets au maximum. Conservé si omis en mise à jour. Non autorisé avec `private_key_jwt`. |
| `client_secret_expires_at` | string \| null | non | Date d'expiration du secret chez le fournisseur d'identité (RFC 3339). Uniquement avec `client_secret`. |
| `provider_profile` | string | non | `entra`, `okta`, `google`, `keycloak`, `ping`, `onelogin`, `jumpcloud` ou `generic` (par défaut). |
| `entra_tenant_id` | string | pour `entra` | L'identifiant de locataire Microsoft Entra (UUID) ; uniquement pour le profil `entra`. |

## Réponse

`201 Created` à la première écriture, `200 OK` en mise à jour, avec la configuration (voir [Récupérer la configuration OIDC](get.md)) et le nouvel `ETag`. Le secret client ne figure pas dans la réponse. Lorsque la mise à jour est devenue une révision en brouillon, le corps contient aussi `revision` (le brouillon), et la configuration et l'`ETag` sont ceux de la version active.

## Erreurs

| HTTP | code | Quand |
|---|---|---|
| 404 | `not_found` | La connexion n'existe pas dans cet environnement ou n'est pas une connexion d'entreprise. |
| 409 | `conflict` | La connexion n'est pas une connexion OIDC, `sp_host` diffère de l'hôte enregistré, ou une méthode à secret n'a pas de secret enregistré ou fourni. |
| 412 | `precondition_failed` | `If-Match` ne correspond pas à la version actuelle, la configuration a été modifiée simultanément, ou `If-Match` a été envoyé avant la première écriture. |
| 422 | `validation_failed` | Un champ ne respecte pas les règles ci-dessus ; `errors[]` indique le champ (`/issuer`, `/client_id`, `/client_auth_method`, `/client_secret`, `/client_secret_expires_at`, `/provider_profile`, `/entra_tenant_id`, `/sp_host`). |
| 428 | `precondition_required` | La configuration existe et l'en-tête `If-Match` est absent. |
| 400 | `unknown_field` | Le corps contient un champ inconnu. |
| 401 | `unauthenticated` | L'identifiant d'accès est absent, invalide ou révoqué. |
| 403 | `insufficient_permissions` | L'identifiant d'accès ne dispose pas de l'autorisation requise. |
| 429 | `rate_limited` | La limite de débit a été dépassée ; réessayez après la durée indiquée par `Retry-After`. |
| 503 | `service_unavailable` | Une dépendance est temporairement indisponible ; réessayez après la durée indiquée par `Retry-After`. |

## Exemple

Conservez le secret dans un fichier, et non sur la ligne de commande (`oidc.json`) :

```json
{"sp_host":"acme.eu1.keenauth.app","issuer":"https://idp.example.com","client_id":"keenauth-acme","client_auth_method":"client_secret_basic","client_secret":"<secret from the identity provider>"}
```

```bash
curl -X PUT "https://api.eu.keenauth.com/v1/connections/conn_01j9xp6q0r1s5t7v9w1x3y5z7b/oidc" \
  -H "Authorization: Bearer $KEENAUTH_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d @oidc.json
```

```bash
keenauth connections oidc put conn_01j9xp6q0r1s5t7v9w1x3y5z7b --data @oidc.json
keenauth connections oidc put conn_01j9xp6q0r1s5t7v9w1x3y5z7b --data @oidc-update.json --if-match '"6f1c…"'
```
