
# Ruota le chiavi di firma

`POST /v1/signing-keys:rotate` · autorizzazione `signing-keys:write`

Esegue subito una rotazione delle chiavi di firma, al di fuori della pianificazione automatica.

- `mode: immediate`: il normale passaggio di rotazione viene eseguito subito. La chiave `next` pre-pubblicata inizia a firmare, la vecchia chiave `active` resta `retired` per 7 giorni e viene pubblicata una nuova chiave `next`. Se `next` usa un algoritmo diverso da quello della chiave attiva (un cambio di algoritmo) ed è stata pubblicata meno di 7 giorni fa, la chiamata fallisce con `409 next_key_too_young`.
- `mode: emergency` (sospetta compromissione della chiave): la vecchia chiave `active` viene revocata ed esce dal JWKS **immediatamente**; `next` diventa attiva e ne viene generata una nuova `next`. `revoke_next: true` revoca anche `next` e attiva una chiave appena generata, mai pubblicata. `revoke_sessions: true` revoca inoltre ogni sessione e ogni famiglia di refresh token dell'ambiente, quindi gli utenti dovranno accedere di nuovo. `reason` è obbligatorio.

## Chiavi di federazione

`use` può anche indicare una delle chiavi di federazione dell'ambiente: `saml_sp_signing` e `saml_sp_encryption` (le chiavi di firma e cifratura del service provider SAML pubblicate nei tuoi metadati SP) oppure `oidc_rp_signing` (la chiave `private_key_jwt` delle connessioni OIDC enterprise, pubblicata in `/sso/jwks.json`). Queste chiavi sono sempre RSA-3072 e ruotano una volta all'anno in automatico: una nuova chiave `next` viene pubblicata 30 giorni prima che subentri, e la vecchia chiave resta pubblicata per altri 30 giorni.

- `mode: immediate` rende subito attiva la chiave `next` pre-pubblicata; la vecchia chiave resta pubblicata per altri 30 giorni (compare in `retired_kids`). Senza una chiave `next` pubblicata la chiamata fallisce con `409 conflict`; se la chiave `next` è stata pubblicata meno di 7 giorni fa, con `409 next_key_too_young`.
- `mode: emergency` rimuove la chiave attiva dai metadati o dal JWKS **immediatamente** e distrugge la sua chiave privata (`revoked_kids`). La chiave `next` pubblicata diventa attiva; se non ce n'è una, oppure con `revoke_next: true`, lo fa una nuova chiave mai pubblicata. I provider di identità che caricano i tuoi metadati o il JWKS dal relativo URL recepiscono la modifica al successivo aggiornamento. Nei provider in cui hai caricato a mano i metadati o il certificato, gli accessi vengono rifiutati finché non carichi il nuovo certificato; in questo caso viene inviato un evento webhook `connection.sp_certificate_rotation_started` per ogni connessione interessata.

Per una chiave di federazione, `next_kid` nella risposta è `null`, `removed_kids` è vuoto e `revoke_sessions` non è ammesso. Se l'ambiente non ha mai usato SAML o `private_key_jwt`, non c'è alcuna chiave da ruotare e la chiamata fallisce con `409 conflict`.

### Chiavi di connessione

Una connessione SAML può avere proprie chiavi SP dedicate invece di quelle dell'ambiente. Per ruotarle, aggiungi `connection_id` (`conn_…`) con `use: saml_sp_signing` o `saml_sp_encryption`. Si applicano le stesse modalità, autorizzazioni e regole, ma cambia solo il set di chiavi di quella connessione: le chiavi dell'ambiente e quelle delle altre connessioni restano invariate, e l'evento `connection.sp_certificate_rotation_started` viene inviato solo per quella connessione. Il record di audit indica l'ambiente come destinazione e la connessione in `metadata.connection_id`. Una connessione sconosciuta, o appartenente a un altro ambiente, fallisce con `404 not_found`; una connessione che usa le chiavi dell'ambiente fallisce con `409 connection_keys_not_dedicated` (ruota le chiavi dell'ambiente senza `connection_id`). Senza `connection_id` viene ruotato il set di chiavi dell'ambiente.

La modalità di emergenza richiede, oltre a `signing-keys:write`, l'autorizzazione `signing-keys:emergency` concessa esplicitamente sulla credenziale: può chiamarla solo una chiave con accesso limitato o un client M2M che abbia quell'autorizzazione. Una chiave segreta con accesso completo (`idp_sk_…`) riceve `403 insufficient_permissions` in modalità di emergenza.

Le nuove chiavi di firma dei token usano l'algoritmo della policy dell'ambiente ([ottieni la policy](/docs/api/management/signing-keys/get-policy)). Una rotazione riuscita della chiave di firma dei token emette l'evento webhook `signing_key.rotated` o `signing_key.revoked`; ogni rotazione scrive un record di audit di gestione. Questo endpoint appartiene alla famiglia di rate limit "emissione di chiavi e segreti". Invia un `Idempotency-Key`: un nuovo tentativo dopo un errore di rete restituisce il primo risultato invece di ruotare due volte.

## Richiesta

Corpo JSON (i campi sconosciuti vengono rifiutati con `400 unknown_field`):

| Campo | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
| `Idempotency-Key` | header | no | Consigliato. 16-64 caratteri di `A-Z a-z 0-9 _ -`. |
| `mode` | string | sì | `immediate` o `emergency`. |
| `use` | string | no | La chiave da ruotare. Predefinito `token_signing` (chiave di firma dei token di accesso e ID). Le chiavi di federazione sono `saml_sp_signing`, `saml_sp_encryption` e `oidc_rp_signing` (vedi [Chiavi di federazione](#chiavi-di-federazione)). |
| `revoke_sessions` | boolean | no | Solo per emergenza e `token_signing`. Revoca anche tutte le sessioni e i refresh token. Predefinito `false`. |
| `revoke_next` | boolean | no | Solo per emergenza. Revoca anche la chiave `next` pubblicata. Predefinito `false`. |
| `reason` | string | emergenza: sì | Solo per emergenza. 1-512 caratteri ASCII stampabili, ad esempio un numero di incidente. Non inserire segreti o dati personali. |
| `connection_id` | string | no | Solo `saml_sp_signing` e `saml_sp_encryption`. Una connessione SAML (`conn_…`) con chiavi SP dedicate; viene ruotato il suo set di chiavi al posto di quello dell'ambiente (vedi [Chiavi di connessione](#chiavi-di-connessione)). |

Con `mode: immediate`, inviare `revoke_sessions: true`, `revoke_next: true` o un `reason` fallisce con `422` (`not_allowed`). Un `connection_id` con `token_signing` o `oidc_rp_signing` fallisce allo stesso modo.

## Risposta

`200 OK` con il risultato della rotazione. `activated_kid` è la chiave che firma da ora in poi e `next_kid` la chiave `next` appena pubblicata (`null` per una chiave di federazione). `retired_kids` sono le chiavi che hanno smesso di firmare e restano pubblicate (7 giorni per una chiave di firma dei token, 30 giorni per una chiave di federazione); `revoked_kids` sono le chiavi rimosse dal JWKS da una rotazione di emergenza; `removed_kids` sono le chiavi ritirate la cui finestra di 7 giorni è terminata e che sono uscite dal JWKS nello stesso passaggio.

```json
{
  "mode": "immediate",
  "use": "token_signing",
  "activated_kid": "ypeBEsobvcr6wjGzmiPcTaeG7_gUfE5yuYB3ha_uSLs",
  "next_kid": "GKw-c0PwFokMUQ6T-TUmEWnZ4_VlQ2Qpgw-vCTT0-OQ",
  "retired_kids": ["PiPoFgA5WUoziU9lZOGxNIu9egCI1CxKy3PurtWcAJ0"],
  "revoked_kids": [],
  "removed_kids": ["Ln0sA6lQeuJl7PW1NWiFpTOTogKdJBOUmXJloaJa78Y"],
  "revoke_sessions": false
}
```

## Errori

| HTTP | codice | Quando |
|---|---|---|
| 400 | `unknown_field` | Il corpo contiene un campo sconosciuto. |
| 400 | `malformed_request` | Il corpo non è JSON valido, oppure l'header `Idempotency-Key` è malformato o ripetuto. |
| 403 | `insufficient_permissions` | La credenziale non ha `signing-keys:write`, oppure è stata richiesta la modalità di emergenza senza l'`signing-keys:emergency` concesso esplicitamente. |
| 404 | `not_found` | `connection_id` non indica alcuna connessione SAML di questo ambiente. |
| 409 | `connection_keys_not_dedicated` | `connection_id` indica una connessione che usa le chiavi SP dell'ambiente. |
| 409 | `next_key_too_young` | Un cambio di algoritmo è in sospeso e la chiave `next` è stata pubblicata meno di 7 giorni fa, oppure (chiave di federazione, `immediate`) la chiave `next` è stata pubblicata meno di 7 giorni fa. |
| 409 | `conflict` | L'ambiente non ha una chiave `next` pubblicata, non ha una chiave attiva della federazione `use`, oppure la precedente chiave di federazione è ancora nella sovrapposizione di 30 giorni. |
| 415 | `unsupported_media_type` | Il content type non è `application/json`. |
| 422 | `validation_failed` | Un campo manca o non è valido; il puntatore indica il campo, ad esempio `/reason`. |
| 422 | `idempotency_key_reuse` | La `Idempotency-Key` è già stata usata con un corpo diverso. |
| 501 | `not_implemented` | L'operazione non è ancora disponibile in questo deployment. |
| 401 | `unauthenticated` | La credenziale manca, non è valida o è stata revocata. |
| 429 | `rate_limited` | È stato superato il rate limit; riprova dopo il periodo `Retry-After`. |
| 503 | `service_unavailable` | Una dipendenza non è temporaneamente disponibile; riprova dopo il periodo `Retry-After`. |

## Esempio

Rotazione normale:

```bash
curl -X POST "https://api.eu.keenauth.com/v1/signing-keys:rotate" \
  -H "Authorization: Bearer $KEENAUTH_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"mode":"immediate"}'
```

Rotazione di emergenza con una chiave con accesso limitato che concede `signing-keys:write` e `signing-keys:emergency`:

```bash
curl -X POST "https://api.eu.keenauth.com/v1/signing-keys:rotate" \
  -H "Authorization: Bearer $KEENAUTH_EMERGENCY_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"mode":"emergency","revoke_sessions":true,"reason":"INC-2041 key exposure"}'
```

Rotazione di emergenza della chiave di firma SAML SP, con revoca anche della chiave `next` pubblicata:

```bash
curl -X POST "https://api.eu.keenauth.com/v1/signing-keys:rotate" \
  -H "Authorization: Bearer $KEENAUTH_EMERGENCY_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"mode":"emergency","use":"saml_sp_signing","revoke_next":true,"reason":"INC-2042 SP key exposure"}'
```

Rotazione di emergenza della chiave di firma SAML SP dedicata di una connessione:

```bash
curl -X POST "https://api.eu.keenauth.com/v1/signing-keys:rotate" \
  -H "Authorization: Bearer $KEENAUTH_EMERGENCY_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"mode":"emergency","use":"saml_sp_signing","connection_id":"conn_01h2xcejqtf2nbrexx3vqjhp41","reason":"INC-2043 connection key exposure"}'
```
