---
title: Rotation des clés
icon: rotate
---

Twenty possède deux familles de clés indépendantes :

* **Clés de signature JWT** — paires de clés asymétriques ES256 (avec balise `kid`) stockées dans `core."signingKey"`, utilisées pour signer et vérifier les jetons d’accès / de rafraîchissement.
* **Clé de chiffrement au repos** — `ENCRYPTION_KEY`, utilisée pour chiffrer les jetons OAuth, les variables d’application, les clés privées de clés de signature, les valeurs de configuration sensibles et les secrets TOTP à l’intérieur d’une enveloppe `enc:v2:`.

`APP_SECRET` est un secret hérité conservé pour la rétrocompatibilité : lorsque `ENCRYPTION_KEY` n’est pas défini, il agit comme mécanisme de secours pour le chiffrement au repos / le cookie de session, et il continue de vérifier les jetons d’accès HS256 préexistants. Il sera déprécié.

## Clés de signature JWT

Chaque clé contient une `publicKey` (conservée indéfiniment afin de pouvoir vérifier les jetons émis précédemment), une `privateKey` chiffrée (utilisée uniquement tant que la clé est actuelle), un indicateur `isCurrent` (exactement une ligne à la fois) et un champ `revokedAt` facultatif.

### Faire pivoter la clé actuelle

Définissez `SIGNING_KEY_ROTATION_DAYS` pour l’activer : une tâche cron quotidienne émet alors une nouvelle clé actuelle dès que l’ancienne dépasse ce seuil. Les clés précédentes ne sont *pas* révoquées, donc les jetons signés avec celles-ci restent vérifiables. Laissez la variable non définie pour désactiver la rotation automatique.

<Note>La rotation automatique est disponible à partir de la v2.6+.</Note>

### Révoquer une clé (fuite / urgence uniquement)

**Settings → Admin Panel → Signing keys → Revoke** sur une ligne non actuelle. Efface le matériel privé chiffré, définit `revokedAt` et rejette chaque jeton existant signé avec ce `kid`.

## Faire pivoter `ENCRYPTION_KEY`

<Note>La commande `secret-encryption:rotate` décrite ci-dessous est fournie à partir de la v2.6+.</Note>

Chaque valeur chiffrée est encapsulée sous la forme `enc:v2:\<keyId>:\<payload>`, où `\<keyId>` est un préfixe hexadécimal sur 8 caractères dérivé de la clé brute. La rotation est en ligne et reprenable.

1. **Générez une nouvelle clé** : `openssl rand -base64 32`.

2. **Configurez les deux clés côte à côte** dans `.env`, puis redémarrez :
   ```ini
   ENCRYPTION_KEY=NEW_VALUE
   FALLBACK_ENCRYPTION_KEY=OLD_VALUE
   ```
   Les nouvelles écritures utilisent la nouvelle clé, les lignes existantes se déchiffrent toujours via la clé de secours.

3. **Rechiffrez les lignes existantes** :

   ```bash
   docker exec -it {server_container} yarn command:prod secret-encryption:rotate
   ```

   La commande parcourt six sites (`connected-account-tokens`, `application-variable`, `application-registration-variable`, `signing-key-private-keys`, `sensitive-config-storage`, `totp-secrets`). Un filtre SQL ignore les lignes déjà sur le nouveau `\<keyId>`, de sorte que la commande est idempotente : interrompez-la et relancez-la si nécessaire. Quitte avec un code de sortie non nul si une ligne échoue — relancez pour réessayer.

   | Option                                   | Description                                                       |
   | ---------------------------------------- | ----------------------------------------------------------------- |
   | `-s, --site \<site>`    | Limiter à un seul site.                                           |
   | `-b, --batch-size \<n>` | Lignes par lot (valeur par défaut `200`, maximum `5000`).         |
   | `-d, --dry-run`                          | Déchiffrer + rechiffrer en mémoire, ignorer la commande `UPDATE`. |

4. **Supprimez la clé de secours** une fois que `--dry-run` n’affiche plus aucune ligne restante : supprimez `FALLBACK_ENCRYPTION_KEY` et redémarrez.

## Prise en charge héritée de `APP_SECRET`

Les instances plus anciennes qui n’ont jamais défini `ENCRYPTION_KEY` utilisent `APP_SECRET` comme clé de chiffrement au repos (et comme secret de cookie de session, dérivé de celle-ci). Ce chemin est conservé pour la rétrocompatibilité mais est **déprécié** — définissez une `ENCRYPTION_KEY` dédiée et suivez la procédure de rotation ci-dessus pour migrer. `APP_SECRET` reste lui-même utilisé pour vérifier les anciens jetons d’accès HS256.
